Gigabyte GA-C1037UN UEFI/BIOS mod: how to load default/optimized settings in case of low CMOS battery


Ok


Clear. Thanks anyway :slight_smile: Can you recommend me someone I can contact who deal with these things?

Our Forum members CodeRush and lordkag know much more about the BIOS modding than me, but I am unsure, if they have the time to help you.


@lordkag @CodeRush I wait for your words of wisdom

However, the real problem is my lack of experience with Ida PRO. It should be easy to find the place where AMITSE display the string "Bios has been reset - …", but I can’t find it.
In addiction, neither me nor Ida PRO do not recognize the starting point of the code, so I cannot use features like the wonderful graph :frowning:

I don’t know if there’s a way to debug the execution of the BIOS. I know that in certain point of the code, the software prints "checkpoints" on I/O port 80, but I cannot find a way they could be userful to me.
Well, there’s a way: I can buy one of the PCI port-80 board, and look at the last checkpoint/word it prints on IO port 80 before if displays the message. If in the code there’s a "out 80h, DISPLAYED_CODE", I could start to investigate from there, right? There are a couple of routines like this one:

1
2
3
4
5
6
7
8
9
10
11
 

seg000:00037B7C sub_37B7C
seg000:00037B7C proc near ; CODE XREF: seg000:00029D6Dp
seg000:00037B7C ; sub_2E958+9Dp ...
seg000:00037B7C mov al, dl
seg000:00037B7E mov dx, cx
seg000:00037B81 out dx, al
seg000:00037B82 retn
seg000:00037B82 sub_37B7C
seg000:00037B82 endp
 
 

Maybe I can follow the point where the software calls sub_37B7C. PS: The code is from an autoanalysis performed by Ida PRO. I still don't know if it's a piece of code or just data that Ida interpreted for unknown reason. However, this snippet seems to have sense.

Guys, you have much much (much) more experience than me. Do you think that what I'm trying to achieve is possible?

EDIT1: I forgot to say that if you need/want access to my dropbox folder where I have all my files (about this project, I mean) I can post the link

I just have pretty much no time now, but I will return to this question tomorrow or in 2 days. Please standby. :slight_smile:

My official reaction to this international crisis is: WAT? And I’m replying in the nicest way possible, no offence intended. You mention of “low CMOS battery” in the title, then move on with saying “after inserting or removing battery”. I’m trying to wrap my head around your issue and all I can see is either a search for the most complicated solution or a self-made problem. Maybe I need to spin my head faster. If the CMOS battery is failing, just replace the damn thing! It is a simple CR2032 battery, can be found at any decent electronics store, for the price of a loaf of bread. By looking at the pictures of your board, there is nothing complicated in replacing it. Listen to the Jedi motivational master, Just Do It! If you are removing the battery for any other reason, may I ask what it is? Do you place it in your pocket when you leave home, do you wash it daily, do you… ? Sorry for the slightly amusing/offensive tone, but I see no reason for removing the CMOS battery, apart from the obvious - reset CMOS. It is my impression that this action also forces the main BIOS chip to be flashed with the content of backup chip, yet another reason to avoid unnecessary physical degradation.

If nothing from above applies and you want to force a selection whenever you reset CMOS, to me it sounds like you are trying to create a non-existent issue: it is offering you a choice, not enforcing one. The string is placed in AMITSE, but AMITSE is made of several sections and IDA can only disassemble the first PE32+ section. There is no direct link (I think) between sections, the code uses a reference to the handlers. You may hunt this “issue” as much as you please, but I’m not interested in spending a single second. Like I said, I fail to see what the issue is, we are on different sides of the bug/feature coin.


I swear I heard in my head Ode To Joy while reading your comment :slight_smile: thank you very much


Sorry about that. My fault. I will explain it better later


You completely missed the point.

This BIOS mod is a part of a larger project that has as an objective to obtain a very reliable machine. This machine - which is supposed to be always on and locked in a closet - must be able to restart in case of an unexpected shutdown (eg. when the power runs out). Most of the time, this machine is able to restart correctly. Other times (eg. CMOS battery low) it appears this prompt ("bios has been…") that prevents my machine from booting without human intervention. Changing the CMOS battery is not a solution because always requires human intervention.

When I’m at home, I simulate the run down of the battery/CMOS reset by removing and inserting again the battery. That’s primary because I don’t want to wait 10 years every time a run a test.


Maybe after reading this explanation you will change your mind.
If you won’t, no problem :slight_smile: I know a man must choose his battles wisely.


I read all your posts and I suppose you worked a lot on AMITSE. If you are interested in spending literally a second on this thread, linking me a resource that goes deep in the module would be beyond my wildest imagination :slight_smile:

The difference between your first post and this one is like between night and day. Yes, for your situation it makes perfect sense to skip that screen and boot without a key press. But try to read it from my side, it sounded like you are either the laziest or the most complicated person alive. Maybe I will look into this in the next days. But don’t expect any miracles, in the case of ME reflash I knew what to search and where, it was more or less easy to patch all three variations of ME recovery/reflash. In your case it might be difficult, as I believe only the handler is invoked and I can’t even be sure if it is from inside AMITSE. Given how this system is important enough to be locked, have you considered Battery Backups/UPS Units? It should at least give you the time or the option to gracefully shutdown the system if the power is cut for longer periods, or to supply the system for short periods of time. Having the BIOS load the defaults and boot without human intervention is the next (not so) best thing. At least the board has DualBIOS, it should recover from any bad patching.

Just to be clear: Gigabyte GA-C1037UN is the board that needs to be patched? And what BIOS version: F2 from rev1.0 or FA from rev2.0? Because the code can change between versions and revisions. Lastly, will you be able to flash any modded BIOS and more than once if needed?


Sorry. My fault. I thought it wasn’t necessary to explain the whole context.


It won’t resolve the problem because there are other cases where the machine has to reboot. For example (but that’s not the only one) when the software freezes, there’s a watchdog timer that forces a reboot.


1. Yes
2. F2 from rev1.0
3. Absolutely. As many times as you want

However, here’s the news:
I found (I know you guys found it long before me) the Aptio source code. In Ivy-Bridge > 018s.zip > Core > EM > AMITSE there’s a sort of documentation in .chm format. Maybe I can get some ispiration from it.

Ok, I tried to make a simple modification to the AMITSE module, changing the string “bios has been…” with another of the same length (actually, I changed “BIOS has” with “INCUDhas”). When I try to replace the module in MMTool, it says “invalid .ffs file”. That’s because I forgot to change something else? It should be MMTool the one that has to change all the checksum, right?
I tried both Insert (“compressed”) and replace (in replace there’s no “as is” and “compressed” options).

I found a PCI Port 80 board. I know that AMI Aptio prints breakpoints while executing the bios.
I forces a CMOS reset, the last breakpoint displayed is 0xAB. According to >this document<, it should be in DXE phase, “Setup Input Wait”. It makes sense.
Here’s the screenshots:
The code displayed by the Port80 PCI board
The famous message shown
PS: I post the link because the images are very big, the width exceeds 1280px
I really don’t know if this will help me. However, “melius abundare quam deficere” says latins.

Finally, the source code fact. I don’t know if I’m totally blind, but I can’t see anything interesting regarding AMITSE. I did not hoped to find the string “bios has been reset” hardcoded in the source (accordingly to the fact that AMITSE is one of the modules customized by the OEMs). Finding an “out 80h, 0xAB” would have been perfect. Then I realized that they’re all C sources. However, I still hope to find that out instruction somewhere.
I uploaded the AMITSE documentation in .chm format >here<, in case someone want to see it. Just told me if it’s a problem and I’ll remove it.

You probably have to correct the Checksum-8 of the FFS file.
For comparison purpose you should check the checksum-8 of the extracted original FFS file.

@incud2

I tried finding some solid ground for the patch, but I can only find bits and pieces. It is as I suspected, there is no pointer/offset to those strings. You can use AMIBCP to remove or add chars from them (thus changing the offset of all following strings), there are no other changes to AMITSE apart from size and checksums. This proves that only a handler will be used, with those 4 strings having the handlers 80h-83h. I am seeing a function where they are all involved, but no proof and no way of knowing what to patch. That AB checkpoint is produced by CORE_DXE, with a generic function:

mov al, cl
out 80h, al ; manufacture’s diagnostic checkpoint (POST code)
out 90h, al

Not much you can use or patch. I have found the place from where the checkpoints are loaded, but the same messy situation. I don’t see an easy way out, not even if you would donate your board for the research cause. The only way I could step further is to have more details. Can you check what are the printed message before and after that selection? I have seen some messages in AMITSE executable section that might be related to this issue, having a solid hook could point me in the right direction.

Hello again

First of all, @lordkag @CodeRush thank you very much for your effords. I really really - really - appreciate that.

Here’s the news: a friend of mine is helping me with the task. He has much more experience than me, and he noticed that if I remove the first bytes until the “MZ” character, my module becomes a PE32+ (Windows 64bit) executable. It makes perfectly sense because UEFI specification were written (also) by Microsoft. I opened it in Ida PRO, and it can start its autoanalysis.

Now the problem is this: the analysis truncates the executable at address 63000 insted of address 85330. The truncated part contains the string “bios has been reset…”. I found on the net that sometimes Ida truncates the file. But that’s not the case: watch the PE Header:

1
2
3
4
 
dd 4CAA0h               ; Size of code
dd 160C0h ; Size of initialized data
...
dd 62E00h ; Size of image
 

Size of the image = 0x62300. That differs from the size of the module. I thought that maybe the last part containing the strings in unicode was a sort of resource file. However, there is no segment .rsrc (according both to Ida and to the PE header). Do you have any idea about how to adjust this things?

[quote="lordkag, post:16, topic:31112"]
This proves that only a handler will be used, with those 4 strings having the handlers 80h-83h.
I am seeing a function where they are all involved, but no proof and no way of knowing what to patch.
[/quote]
Sorry but I didn't understand these sentences. Could you explain it in a simplier way?

[quote="lordkag, post:16, topic:31112"]
That AB checkpoint is produced by CORE_DXE, with a generic function:
[/quote]
In your opinion, is there the possibility that the part of the BIOS that displays the message is in CORE_DXE? It could be possible, right?

[quote="lordkag, post:16, topic:31112"]
Can you check what are the printed message before and after that selection?
[/quote]
If you mean printed on the screen, nothing is displayed apart the Gigabyte logo.
If you mean the checkpoint printed on port80, tomorrow I'll write here the ordered list of printed checkpoint.

Thank you very much in advance

This is what I was saying in the first message. If you open your BIOS file in UEFITool and search for AMITSE in Unicode, you will see that the file has several sections. This is a standard for AMI, not a special case for your board. From those sections, only the body of PE32 can be analysed by IDA, there is no direct link to the others. From IDA point of view, the rest is just padding/garbage. To reference them, AMITSE is using (I guess) the handler of the string. To find this connection, you have to search for 80h, 81h, 82h, 83h - nothing more than that to use. You can use AMIBCP to alter the length of the strings, nothing will change in the code, evidence that only the handler is used for referencing. If you search for hex E609E497C14CD91181F6000000000000 (the GUID that stores your message), only CORE_DXE seems to load it. But there are more than one sections with that GUID, not sure which one is loaded.

Yes, it is my guess that CORE_DXE has a part in displaying that message and will be involved in patching. What I really need is the messages displayed on screen. Try to disable all UEFI options, leave it to legacy and slow Boot, to have the POST messages. There has to be something printed, besides that reset message.


1. I realize it just right now
2. Handler = address of the string?
3. How did you find that numbers?
4. Did I understand something wrong, or each string has a GUID? How can I find it?
5. I can see it. CORE_DXE, Reflash, AMITSE, Setup (category: DXE driver) and another unnamed (category: freeform). Maybe, I should extract and give a look at all of them
6. Surely I will.

EDIT (about point 6):
Just a note: it I force a CMOS battery fault, shouldn’t it ask me to load defaults BEFORE showing the POST? In this case, isn’t it probably that POST considers the "CMOS battery fail" resolved (I decided to load the defaults) and won’t display anything?

  1. When you open the file in AMIBCP, each string has a handle (Setup Configuration) or a token (BIOS Strings). Your desired strings are in the range B2h-B5h when all setup strings are concatenated and displayed by AMIBCP. If you use the options “Extract Strings”, they will be in range 80h-83h. I’m thinking that AMITSE will only deal with the strings it contains, thus 80h-83h seems more likely. AMIBCP can extract this handle/token from file, it can also display the setup strings in the right visual order, even though the tokens and strings are stored in a different order. I’m guessing that AMIBCP can access these values from a table or something that contains the handles/tokens. If this is not the case, then I don’t know what else to follow.
    3. See above.
    4. The strings are placed in a GUIDed section. That GUID is 97E409E6-4CC1-11D9-81F6-000000000000 or E609E497C14CD91181F6000000000000 in hex.
    6. I don’t know what your system does or displays. That is why I asked you to look into the messages. Having a message that can be traced by IDA could narrow the field of search.
    7. Please understand that my time is limited, I have no obligation to help you, I’m not a patient man. Asking questions that have no relevance in solving this problem are only taking from the time I dedicate to your issue. We haven’t even located the responsible code, wasting time with small details is wasting time. Providing the details to my request (nr 6.) is the most (and only) lucrative approach.


I understand that completely. I really appreciate your help, and it’s not my intetion neither to test your patient nor to take advante of your help. However, I don’a agree with the last sentence. These small details helped me to continue this work a bit more independently so that’s not a waste of time from my point of view, but a huge advantage.
Without your help I would still be at the beginning of this task, but without my contribution it becomes your work, not mine.


Well, I tried to open AMIBCP > "Extract Strings", I can see the handle/token is 80h (absolute 00B2h) but it’s in the GUID [B1DA0ADF-4F77-4070-A88E-BFFE1C60529A] (the AMIBCP one), not in E609E497C14CD91181F6000000000000.

I tried on UEFITool and I found:

1
 
Unicode text "bios has been reset" found in 97E409E6-4CC1-11D9-81F6-000000000000 at offset 175Ch
 

And that's ok, but not in E609E497C14CD91181F6000000000000

However, our point 6:
Here's the list of CHECKPOINT it prints before the message:
--, [too fast, 0x18 I think - I filmed it with the smartphone but that's too fast even for it], 0x15, 0x3B, 0x32, 0x4F, [2-3 very fast], 0x62, 0x88, 0x72, [...], 0x99, 0x88, 0x9C, [...], 0xB2, 0x24, 0xA2, 0xA9, 0xAB. Some where so fast that I weren't able to film them. However, it freezes at 0xAB, it displays the message and waits for the input.
On the screen, it does not display absolutely nothing. Before the message, black screen.

EDIT:
I've got CORE_DXE source. Do you think it's reasonable to look in the source before, and in the disassembled code then? OEM should not customize this module, right? The code is >here< in Core > CORE_DXE

The more time I spend on CORE_DXE, the more I belive that the module we need to modify is this.

CORE_DXE contains HII (human interface infrastructure) implementation, the UEFI protocol that manages with strings and user interface in general (AMITSE does another thing in my opinion - it only displays the strings). Here’a an >explanation of HIIS<. The author said:



Here’s the HII String Protocol (the explanation of the specification): >link<
Here’s the HII Database: >link<
And here’s the code in HiiString.c, that shows the way CORE_DXE module > HII functions retrives the string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
//----------------------------------------------------------------------------
// Procedure: AmiHiiGetString
//
// Description:
// This function is HII String protocol function GetString implementation
//
// Input:
// IN EFI_HII_STRING_PROTOCOL *This - Pointer to EFI_HII_STRING_PROTOCOL structure
// IN CHAR8 *Language - Language definition
// IN EFI_HII_HANDLE PackageList - Handle of package list
// IN EFI_STRING_ID StringId - String Id
// OUT EFI_STRING String - Pointer to output string
// IN OUT UINTN *StringSize - Pointer to string size value
// OUT EFI_FONT_INFO *StringFontInfo - Pointer to returned string font info
//
// Output:
// EFI_SUCCESS - Function executed successfully
// EFI_INVALID_PARAMETER - String or StringSize or Language is NULL
// EFI_NOT_FOUND - String not found
// EFI_BUFFER_TOO_SMALL - Allocated buffer too small
//
//----------------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS AmiHiiGetString(
IN CONST EFI_HII_STRING_PROTOCOL *This,
IN CONST CHAR8 *Language,
IN EFI_HII_HANDLE PackageList,
IN EFI_STRING_ID StringId,
OUT EFI_STRING String,
IN OUT UINTN *StringSize,
OUT EFI_FONT_INFO *StringFontInfo OPTIONAL)
{
PACKAGE_LIST_RECORD *Record = (PACKAGE_LIST_RECORD *)PackageList;
STRING_RECORD DbKey;
INT8 Vicinity;
STRING_RECORD *Found;
UINTN *FindRecord;
UINT32 Size;
EFI_STATUS Status;
LANGUAGE_LINK *Ptr;
UINT32 i;
UINT16 SaveLanguage;
 
if (Language == NULL || StringId < 1 || StringSize == NULL || PackageList == NULL)
return EFI_INVALID_PARAMETER;
 
if (String == NULL && *StringSize != 0)
return EFI_INVALID_PARAMETER;
 
if (Record->Signature != PACKAGE_LIST_RECORD_SIGNATURE)
return EFI_NOT_FOUND;
 
DbKey.StringId = StringId;
Status = GetLanguageId(Record, Language, &(DbKey.LanguageId), &Ptr);
if(EFI_ERROR(Status))
return EFI_INVALID_LANGUAGE;
 
Status = DbeLocateKey(&(Record->StringDb), 0, &DbKey, &FindRecord, &Vicinity, NULL);
if(EFI_ERROR(Status))
{
//string not found in database, try different languages
SaveLanguage = DbKey.LanguageId;
for(i = 0; i < Record->LanguageList.Size; i++)
{
if (i == SaveLanguage)
continue;
DbKey.LanguageId = (UINT16)i;
Status = DbeLocateKey(&(Record->StringDb), 0, &DbKey, &FindRecord, &Vicinity, NULL);
if(!EFI_ERROR(Status))
return EFI_INVALID_LANGUAGE; //string exists but with different language
}

return EFI_NOT_FOUND;
}
 
Found = (STRING_RECORD *)(UINTN)(*FindRecord);
 
Size = StrSize16(Found->String);

if(Size > *StringSize)
{
*StringSize = Size;
return EFI_BUFFER_TOO_SMALL;
}
 
StrCpy16(String, Found->String);
*StringSize = Size;
 
return EFI_SUCCESS;
}
 


Another part of CORE_DXE, ConSplitter (I don't know what the name means) seems to expose high-level function to get data from keyboard (CSReadKeyStrokeEx, CSWaitForKey, ...)

Finally, I think I found the the code/macros that should print the breakpoints. Look at here (at the notes):
Function : checkpoint
File: x64AsmLib.asm
Description: VOID checkpoint(IN UINT8 c) writes the value c to port 0x80.
Input: IN UINT8 c
Notes: This routine should only be used if the PROGRESS_CODE or PEI_PROGRESS_CODE macros are unavailable.

While I agree that providing extra details can help you to follow / participate in this research, or even help you to solve / finding a trace to the solution, it still doesn’t change the fact that I have to reply to questions that seem elementary in my book.

B1DA0ADF-4F77-4070-A88E-BFFE1C60529A is AMITSE. The name is just for human readability, the machine only knows and cares about the GUID. All strings are placed in sections with GUID 97E409E6-4CC1-11D9-81F6-000000000000, how it would help to have that repeating X times in the saved strings? The parent file is more important and unique in a volume, thus it is used. 97E409E6-4CC1-11D9-81F6-000000000000 is E609E497C14CD91181F6000000000000 in hex string, how else do you store that GUID in binary form? The questions to the above answers is the exact thing I tried to avoid. They can be easily answered with simple logic and EFI basics, they bring nothing new or helpful to the table.

The codes 0xB2 - Legacy Option ROM Initialization, 0xA2 - IDE Detect, 0xA9 = Start of Setup can be used to trace the code a little better than 0xAB - Setup Input Wait, because those codes are linked to more complex and better documented actions. However, here is the bad news. Without having an immediate and secure hook to base my research, I will have to decline or heavily postpone this task. Having the source code is not as important as it sounds, because: a) I have to read it thoroughly and b) understand it. Which is not the case, as: a) I’m deeply covered in some personal projects and b) I can understand C code, but not cover complex sources. Even if I had the time and skill to cover this first step, I would hit wall number two: trying to match this code to some assembly instructions. Which is not the case, again, as I don’t have: a) enough time and b) enough knowledge. I can understand basic instructions, but not convert from C code to assembly or vice-versa.

I’m not sure that following generic functions like the ones from HiiString or ConSplitter would help. Even if you find them in IDA, how would you link them to your issue? You should search something that it is as close as possible to that 0xAB checkpoint, while still being easily traceable in IDA (or your favourite disassembler). It gets even more complicated when you don’t know which module to inspect: is it AMITSE, CORE_DXE, any other involved? For instance, AMIBCP is loading the tree (most likely) from the third section of AMITSE. The keywords to search next in sources is STRING_TOKEN and $SPF. But as I said, you need someone that knows C/C++, knows the sources, knows assembly, has the time and will to implement a patch. Humanity’s last hope may be in CodeRush. And the joke is not so much of a joke, as there are only a few independent researchers investigating UEFI (to my knowledge), even fewer that would provide a patch for a specific issue, for a specific user. This brings me to what should have been my first question: have you tried asking Gigabyte for a patched BIOS?


Yes, I asked both AMI and Gigabyte. AMI replied that they cannot help me, while Gigayte didn’t answer.


I knew from the beginning that this is the hardest task I’ve ever have to deal with. I completely understand why you have to decline this task. I have nothing to say other than thank you for your time - most of the people would not even have started this task. I think I’ll continue to try, maybe I’ll still continue to publish my progresses on this thread.

Just another question: I’ve another idea, probably my last trump card. Tell me (@lordkag ) if in your opinion, this makes sense:
I thought I might add some INT 3h (breakpoint) in modules AMITSE and CORE_DXE. I will first put one breakpoint early in the code and try to boot:
1. if does not show the message
1.A. remove breakpoint
1.B. add breakpoint later in the code
2. flash & try until you see the message. That will be the point involved
What do you think?

EDIT: I forgot INT 3h is a software breakpoint. Is it better to use hardware breakpoint (DR0-DR3)?

How do you add INT 3 to a compiled file and where do you add it, in your specific case? This is not so much a question towards you, but rather a rhetorical one, as I have no knowledge of doing that.