GRUB2 and poking MMIO registers? (The Wiki says so)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
cloudsdale
Posts: 5
Joined: Tue Jun 11, 2019 6:35 pm

GRUB2 and poking MMIO registers? (The Wiki says so)

Post by cloudsdale »

In the Intel HD Graphics Wiki article, there's a puzzling fragment that says:
Experimenting with the device
There are various tools available to study the behavior of the graphics device for simpler tasks in order to gain an understanding of how something is done. One way to do so is to use Intel's graphics debugging utilities (The intel-gpu-tools package), which allow reading and writing to the device registers from the terminal. This can be very useful, for instance, this method proved invaluable when studying the GMBUS. The same can be done using GRUB2’s built in terminal by obtaining the MMIO base address using lspci and then using the read/write commands to perform the associated operation on the desired register.
Does anyone know what read/write commands are they talking about? I dug through GRUB's documentation and I couldn't find anything related to reading/writing MMIO memory (or any memory whatsoever). There are no read/write listed in the commands list. Are there some undocumented commands I should be aware of, or the Wiki is simply lying? :^o

I have to admit, poking MMIO registers from the boot menu / terminal could be useful for experimenting with the hardware, so it would be great if this feature were real. But the Wiki article doesn't seem to provide any details about that, neither I could find them anywhere else in the Wiki nor on the Forums (nor anywhere else in the Net, for that matter).

Any ideas what they might be talking about in that article?
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by iansjack »

Try:

"help read"

and

"help write"

at the grub2 terminal prompt. The commands you are looking for are write_byte, write_word, and write_dword.

You are correct that these commands aren't documented in the manual and I can't find them documented anywhere else. If you want to see the full list of commands, type "set pager=1", then "help". You can then type "help <command>" to get more detailed help about an individual command.
User avatar
hgoel
Member
Member
Posts: 89
Joined: Sun Feb 09, 2014 7:11 pm
Libera.chat IRC: hgoel
Location: Within a meter of a computer

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by hgoel »

Funny coincidence that this gets posted now, but yes, as iansjack said, I was referring to write_byte/write_word/write_dword and read_byte/read_word/read_dword. I updated that bit in the article to say so.
"If the truth is a cruel mistress, than a lie must be a nice girl"
Working on Cardinal
Find me at [url=irc://chat.freenode.net:6697/Cardinal-OS]#Cardinal-OS[/url] on freenode!
User avatar
cloudsdale
Posts: 5
Joined: Tue Jun 11, 2019 6:35 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by cloudsdale »

iansjack wrote:Try "help read" and "help write" at the grub2 terminal prompt.
Wow, that was quick! :> And indeed, these commands work. Thanks! :)
Too bad that they aren't documented on their website though #-o
(And I'm so dumb that I didn't think about trying "help" :oops:)

Hmm… The command line seems to work fine on my own pendrive with GRUB2 installed with "grub-install". But when I tried a couple of LiveUSB distros, they only display a "boot:" prompt that doesn't seem to recognize any of these commands (including "help"). Pressing the "C" key also doesn't work. Is there any way to launch the GRUB console from those? Or did they block it somehow?
hgoel wrote:Funny coincidence that this gets posted now
What's the coincidence about?
hgoel wrote: updated that bit in the article to say so
Great! One landmine disarmed :)

OK, so time for some bragging, for some context of why I needed this :mrgreen:

I'm experimenting with a couple of different video chips I have on my machines, trying to make them listen and do some display detection, mode switching etc., to gather some intel that will come handy when writing video drivers.

I started with ATI/AMD Radeon Xpress RS690M that I had on one old HP laptop because I won't cry if I break it :q AMD chips are documented quite well, so it will be good for testing as well.

I got the MMIO ranges from lspci, booted GRUB2 from a pendrive, and started using those commands for reading the values of different registers in order to see if they contain what the specs say. I managed to read some Vendor/Product IDs to confirm that I'm dealing with the right graphics chip, and then flipped some bits on the GPIO that turn on/off the panel backlight, and IT WORKED! :D The display turned off, but when I typed in the command from memory (because I couldn't see anything), it turned on again and I could see what I typed :mrgreen:

So yeah, first steps in talking directly to a real display hardware has been made 8)

Now I'll need to figure out how to read the display's EDID info through that GPIO_DDC_DATA_A pin :-k
There's only one pin, so I guess that I'd have to read it bit by bit or something? :q

But that's another story, so I'll probably post a separate topic about that if I won't figure that out myself.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by Octocontrabass »

cloudsdale wrote:Now I'll need to figure out how to read the display's EDID info through that GPIO_DDC_DATA_A pin :-k
There's only one pin, so I guess that I'd have to read it bit by bit or something? :q
It's I²C, so you need at least two pins. I found a datasheet for a similar (possibly the same) GPU that says to use I2C_CLK with DDC_DATA.
User avatar
cloudsdale
Posts: 5
Joined: Tue Jun 11, 2019 6:35 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by cloudsdale »

May I take a look at that datasheet too?

I remember reading somewhere that on old Nvidia cards, the DDC signal was allegedly triggered by VSYNC signal and the monitor was sending the EDID data each frame again and again in a loop. Then on later cards there was a separate pin in the VGA connector that clocked the DDC.

I see in the specs (page 31, or 37 in the PDF) that there are some I²C ports here as well, although I'm not sure if they're solely for DDC. Nvidia cards, for example, use I²C also for communication with other subsystems too, and there's some shunting involved.
User avatar
hgoel
Member
Member
Posts: 89
Joined: Sun Feb 09, 2014 7:11 pm
Libera.chat IRC: hgoel
Location: Within a meter of a computer

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by hgoel »

cloudsdale wrote:
hgoel wrote:Funny coincidence that this gets posted now
What's the coincidence about?
I hadn't touched osdev in a while, so seeing a post about something I wrote the day after came back was funny timing :p
I'll probably get around to updating that article once I get through refactoring my old code.
cloudsdale wrote: So yeah, first steps in talking directly to a real display hardware has been made 8)

Now I'll need to figure out how to read the display's EDID info through that GPIO_DDC_DATA_A pin :-k
There's only one pin, so I guess that I'd have to read it bit by bit or something? :q

But that's another story, so I'll probably post a separate topic about that if I won't figure that out myself.
Nice! It doesn't look like there's any interface similar to the GMBUS on Intel to automate things, so you're probably going to have to 'bit-bash' to use the I2C. The easiest way to figure this out would be to dig through the driver for a BSD or for Linux. It would be nice if you could add an article to the wiki with your discoveries, basic display management isn't really all that difficult up to ~2010s eta graphics cards, it's just a matter of digging through lots of code and documenting things.
"If the truth is a cruel mistress, than a lie must be a nice girl"
Working on Cardinal
Find me at [url=irc://chat.freenode.net:6697/Cardinal-OS]#Cardinal-OS[/url] on freenode!
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by Octocontrabass »

cloudsdale wrote:May I take a look at that datasheet too?
Here you go.
cloudsdale wrote:I remember reading somewhere that on old Nvidia cards, the DDC signal was allegedly triggered by VSYNC signal and the monitor was sending the EDID data each frame again and again in a loop. Then on later cards there was a separate pin in the VGA connector that clocked the DDC.
Aha, supporting the old DDC1 protocol would explain the need for separate DDC_DATA and I2C_DATA pins.
User avatar
cloudsdale
Posts: 5
Joined: Tue Jun 11, 2019 6:35 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by cloudsdale »

OK, i managed to talk to an external display through the wire by bit-banging the graphics card's GPIO registers in GRUB2's console. It seems to be returning the correct data, so I guess that counts as the first step to success :mrgreen:

But gosh! In order to get the correct addresses for those registers, I had to manually parse a VBIOS image, as well as dig through the Linux kernel sources to figure out the format of all the AtomBIOS data structures, and stuff the kernel module with my own debug messages to peek the values of different variables it uses in the process. What a mess! :evil:

Anyway, now I'd like to reproduce the same thing in my bootable code so that I didn't have to type in all those commands in GRUB2 and transfer everything bit by bit. If I understand correctly, when I'm already in protected mode, I can just read/write those (physical) memory areas (0xD0200000 in my case is the base address of the MMIO for my Radeon, + the offsets of the particular registers I want to write/read), right?

But now I feel a problem coming:
The I²C bus used for DDC has a certain maximum speed limit, which is 100 kbps in the standard mode. If I start bit-banging the registers from my code, I presume it will work too fast :P So I need some way to introduce a delay between subsequent bits. What would be the simplest way to do that?

My first thought was to just call a subroutine that would do a tight loop with some big number of iterations. Although rather simple to do, I don't think it is a good idea to burn the CPU when all you do is waiting between sending bits :q Also it might not be very accurate, because it would depend on the speed of execution of that loop on a particular CPU. (Although DDC can do fine with slower clock rates, so it won't be that much of a problem, I guess, provided that I send bits at a lower rate than those 100 kbps :-k )

Another thought I had was to use rdtsc instruction, but I guess that would have the same problem as the loop, since the CPU's internal timestamp counter counts CPU cycles, not actual time that passed. So it also depends on the execution speed.

Is there any other counter on a PC that I could use, that would count actual time? (Let's say that I need to check if at least 100 microseconds has passed since I last checked.)

Or maybe some sort of an interrupt that fires every X microseconds?
That would be even better, I suppose, because then I could just hang the CPU and let the interrupt wake it up and send the next bit, then call me back after it finishes.

How do you usually do such delays in your system code?
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by Ethin »

The RDTSC/RDTSCP instructions are constant invariant on modern processors, so they don't change when the processors clock speed changes due to things like power events and such, so yes, that's a good time source (I think both Linux and Windows use it). The RDTSCP instruction is serializing whereas the RDTSC instruction is not. As an alternative to the TSC, you could use the HPET or PIT.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: GRUB2 and poking MMIO registers? (The Wiki says so)

Post by Octocontrabass »

cloudsdale wrote:If I understand correctly, when I'm already in protected mode, I can just read/write those (physical) memory areas (0xD0200000 in my case is the base address of the MMIO for my Radeon, + the offsets of the particular registers I want to write/read), right?
Right. Keep in mind most programming languages are designed around the assumption that you're accessing ordinary memory by default, and those assumptions won't work with MMIO.
cloudsdale wrote:If I start bit-banging the registers from my code, I presume it will work too fast :P
Maybe. Some bus protocols allow forcing the CPU to wait if it tries to issue bus transactions faster than the destination hardware allows.
cloudsdale wrote:So I need some way to introduce a delay between subsequent bits. What would be the simplest way to do that?
A delay loop. But as you've already noticed, simplest isn't always best.
cloudsdale wrote:Another thought I had was to use rdtsc instruction, but I guess that would have the same problem as the loop, since the CPU's internal timestamp counter counts CPU cycles, not actual time that passed. So it also depends on the execution speed.
Modern CPUs have constant TSC frequency, so you only need to calibrate it against a timer with a known frequency to use an RDTSC loop. You'll probably also want to use PAUSE as a hint to the CPU that it shouldn't try running the loop as fast as possible. (It might be possible to detect the TSC frequency using CPUID...)
cloudsdale wrote:Is there any other counter on a PC that I could use, that would count actual time? (Let's say that I need to check if at least 100 microseconds has passed since I last checked.)
Yeah, there are a bunch (on modern PCs), including the PIT, RTC, HPET, APIC, and probably some others I'm forgetting at the moment. I don't think any of them would be better than fixed-frequency RDTSC here.
cloudsdale wrote:Or maybe some sort of an interrupt that fires every X microseconds?
All four of the aforementioned timers can also raise interrupt requests, but if the interrupts are too frequent you're not going to see any benefits over an RDTSC loop. Bit-banging 100kbps would take a lot of interrupts...
Post Reply