mirror of
https://codeberg.org/libreboot/memtest86plus.git
synced 2025-07-04 00:07:05 +00:00
Compare commits
147 commits
v6.00-beta
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
5dcd424ea7 | ||
|
1f1fe5bfe8 | ||
|
0fd2e4c37a | ||
|
fa4e903509 | ||
|
bfbb167a72 | ||
|
79bb781431 | ||
|
c6b04e5414 | ||
|
5cbcd2046b | ||
|
bf0dae04bc | ||
|
dcca756e48 | ||
|
262aac4f85 | ||
|
66bd82f12a | ||
|
ee0c400821 | ||
|
e1fc02bfe0 | ||
|
a1d046fc3a | ||
|
1a38f513de | ||
|
22663f89bb | ||
|
c38b0cbc5f | ||
|
dfc41f7196 | ||
|
f62bbfde32 | ||
|
8305d47675 | ||
|
a47f681151 | ||
|
68e9542c1e | ||
|
ce2c29eddc | ||
|
b01c8e4388 | ||
|
d088740757 | ||
|
9c16a0568a | ||
|
040e253b54 | ||
|
2fa2346ae0 | ||
|
e5d7119abf | ||
|
a4c9adc445 | ||
|
485bfa46a3 | ||
|
b15a8bb632 | ||
|
327495ec61 | ||
|
8f788b27e1 | ||
|
f24e897883 | ||
|
3aeda70e24 | ||
|
b6992b9ec0 | ||
|
10e8435604 | ||
|
186ef6e913 | ||
|
d0399fd287 | ||
|
68deff493f | ||
|
1fca6dbcab | ||
|
a1af48a8cf | ||
|
03cd8d1898 | ||
|
d3d52b8a11 | ||
|
04980dfda3 | ||
|
d1014365c1 | ||
|
e022441544 | ||
|
3dd1fa8959 | ||
|
e3c0d6df60 | ||
|
87f03f3b10 | ||
|
f96c5b5093 | ||
|
59a0996070 | ||
|
da7b9b955d | ||
|
036922ab26 | ||
|
5a2bc4c960 | ||
|
9a86f115f4 | ||
|
0beb172a0d | ||
|
d3bc8fa7c2 | ||
|
1ee1078cf5 | ||
|
5036aa197a | ||
|
1278f02617 | ||
|
85213a9a27 | ||
|
83d08db69e | ||
|
aaa0cffaa6 | ||
|
66b1389348 | ||
|
407fb811c2 | ||
|
ddbee66c85 | ||
|
1c4d7f4089 | ||
|
c41159084d | ||
|
03a5222ee2 | ||
|
f265d1f1c5 | ||
|
18f12116c0 | ||
|
385f912776 | ||
|
6799bfba3c | ||
|
540270513f | ||
|
5686da4b1d | ||
|
43b0f64ddb | ||
|
0f8981412c | ||
|
93051adfc2 | ||
|
02702fa8c5 | ||
|
9660eead4e | ||
|
148dfd4d54 | ||
|
1316c6c099 | ||
|
8a3cac8133 | ||
|
408fdb8db6 | ||
|
13d9569041 | ||
|
740df34656 | ||
|
cf156adc4a | ||
|
53f61e6b87 | ||
|
37cb966f39 | ||
|
187bc8609e | ||
|
034372f4bf | ||
|
e6e0f0c8e7 | ||
|
89e2643de4 | ||
|
c2e94527e1 | ||
|
6b998e82e7 | ||
|
1c88824a7d | ||
|
680e6ad79b | ||
|
231b389b3c | ||
|
a5576974cf | ||
|
eac4d03462 | ||
|
cee2d32766 | ||
|
221a66da1a | ||
|
63a07258fd | ||
|
7758adfb9d | ||
|
106eabef98 | ||
|
e86b04a14a | ||
|
6cd356f831 | ||
|
9ed7e22091 | ||
|
c9f36fd4f5 | ||
|
fa563a8cb2 | ||
|
e154320790 | ||
|
fae2ae9e13 | ||
|
e1508a02be | ||
|
69aeda1c27 | ||
|
7c969db4da | ||
|
1383f871d8 | ||
|
67cb78f4e5 | ||
|
0adcdfa160 | ||
|
4b17a4cc0c | ||
|
3a107018e5 | ||
|
c6ff7b1486 | ||
|
cd68333f86 | ||
|
438201195d | ||
|
dce2cfb079 | ||
|
3bc72c1fd4 | ||
|
e0cee8e2c3 | ||
|
6b253adb9a | ||
|
bc8235f50d | ||
|
d901f9e8a1 | ||
|
caa07482a0 | ||
|
c2d033b4b4 | ||
|
076e133415 | ||
|
c1d0c17f23 | ||
|
37edb221d7 | ||
|
f16be176ed | ||
|
f98ceb1613 | ||
|
722b1b2899 | ||
|
c56fbe3257 | ||
|
e8c8ef69eb | ||
|
ef3f0bc1e5 | ||
|
e429abec9f | ||
|
a67d346ef7 | ||
|
af54067228 | ||
|
7b41830c40 |
81 changed files with 4512 additions and 1954 deletions
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Set update schedule for GitHub Actions
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
interval: "daily"
|
63
.github/workflows/Linux.yml
vendored
Normal file
63
.github/workflows/Linux.yml
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
name: Build and tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
paths-ignore:
|
||||
- "**/README.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Linux Dependencies
|
||||
run: sudo apt-get install build-essential gcc-multilib clang libc6-dev-i386-cross dosfstools mtools xorriso -y
|
||||
|
||||
- name: Make all build32
|
||||
env:
|
||||
C: ${{matrix.compiler}}
|
||||
working-directory: ./build32
|
||||
run: make -j 2 all
|
||||
|
||||
- name: Make clean build32
|
||||
working-directory: ./build32
|
||||
run: make clean
|
||||
|
||||
- name: Make all build64
|
||||
env:
|
||||
C: ${{matrix.compiler}}
|
||||
working-directory: ./build64
|
||||
run: make -j 2 all
|
||||
|
||||
- name: Make clean build64
|
||||
working-directory: ./build64
|
||||
run: make clean
|
||||
|
||||
- name: Make iso build32
|
||||
env:
|
||||
C: ${{matrix.compiler}}
|
||||
working-directory: ./build32
|
||||
run: make -j 2 iso
|
||||
|
||||
- name: Make iso build64
|
||||
env:
|
||||
C: ${{matrix.compiler}}
|
||||
working-directory: ./build64
|
||||
run: make -j 2 iso
|
22
.github/workflows/expired.yml
vendored
Normal file
22
.github/workflows/expired.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 */2 * *'
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-milestones: 'future,alpha,beta,release'
|
||||
exempt-pr-milestones: 'bugfix,improvement'
|
||||
exempt-all-pr-assignees: true
|
||||
stale-issue-message: 'This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 30 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 30 days with no activity.'
|
||||
days-before-issue-stale: 120
|
||||
days-before-pr-stale: 120
|
||||
days-before-issue-close: 30
|
||||
days-before-pr-close: 30
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,7 +8,7 @@
|
|||
*.o
|
||||
|
||||
# Generated file
|
||||
githash.h
|
||||
build_version.h
|
||||
|
||||
# Binaries
|
||||
memtest_shared
|
||||
|
|
104
README.md
104
README.md
|
@ -1,19 +1,21 @@
|
|||
---
|
||||
[DISCLAIMER] Memtest86+ v6.0 is NOT READY FOR PRODUCTION yet. The base code has basically been rewritten from scratch and many side functions are still under active development. A lot of additional beta-testing is needed. Please consider the actual code as experimental and expect crashes and freezes. Bugs reports are welcome and very helpful! The first binary beta release is planned for April (along with a new [memtest.org](https://memtest.org) website) and a first production-ready stable release this summer.
|
||||
|
||||
---
|
||||
|
||||
# Memtest86+
|
||||
|
||||
Memtest86+ is a stand-alone memory tester for x86 and x86-64 architecture
|
||||
computers. It provides a more thorough memory check than that provided by
|
||||
BIOS memory tests.
|
||||
Memtest86+ is a free, open-source, stand-alone memory tester for x86 and
|
||||
x86-64 architecture computers. It provides a much more thorough memory
|
||||
check than that provided by BIOS memory tests.
|
||||
|
||||
It is also able to access almost all the computer's memory, not being
|
||||
restricted by the memory used by the operating system and not depending
|
||||
on any underlying software like UEFI libraries.
|
||||
|
||||
Memtest86+ can be loaded and run either directly by a PC BIOS (legacy or UEFI)
|
||||
or via an intermediate bootloader that supports the Linux 16-bit, 32-bit,
|
||||
64-bit, or EFI handover boot protocol. It should work on any Pentium class or
|
||||
later 32-bit or 64-bit CPU.
|
||||
|
||||
Binary releases (both stable and nightly dev builds) are available on
|
||||
[memtest.org](https://memtest.org).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Origins](#origins)
|
||||
|
@ -28,23 +30,24 @@ later 32-bit or 64-bit CPU.
|
|||
* [Memtest86+ Test Algorithms](#memory-testing-philosophy)
|
||||
* [Individual Test Descriptions](#individual-test-descriptions)
|
||||
* [Known Limitations and Bugs](#known-limitations-and-bugs)
|
||||
* [Code Contributions](#code-contributions)
|
||||
* [Acknowledgments](#acknowledgments)
|
||||
|
||||
## Origins
|
||||
|
||||
Memtest86+ v6.0 was based on PCMemTest, which was a fork and rewrite of the
|
||||
earlier Memtest86+ v5.01, which in turn was a fork of Memtest86. The purpose
|
||||
Memtest86+ v6.00 was based on PCMemTest, which was a fork and rewrite of the
|
||||
earlier Memtest86+ v5, which in turn was a fork of MemTest-86. The purpose
|
||||
of the PCMemTest rewrite was to:
|
||||
|
||||
* make the code more readable and easier to maintain
|
||||
* make the code 64-bit clean and support UEFI boot
|
||||
* fix failures seen when building with newer versions of GCC
|
||||
|
||||
In the process of creating PCMemTest, a number of features of Memtest86+ v5.01
|
||||
In the process of creating PCMemTest, a number of features of Memtest86+ v5
|
||||
that were not strictly required for testing the system memory were dropped. In
|
||||
particular, no attempt is made to measure the cache and main memory speed, or
|
||||
to identify and report the DRAM type. These features will be added back
|
||||
in Memtest86+ v6.0 in an attempt to create an unified, fully-featured release.
|
||||
particular, no attempt was made to measure the cache and main memory speed, or
|
||||
to identify and report the DRAM type. These features were added back and expanded
|
||||
in Memtest86+ v6.0 to create a unified, fully-featured release.
|
||||
|
||||
## Licensing
|
||||
|
||||
|
@ -105,8 +108,10 @@ For test purposes, there is also an option to build an ISO image that uses
|
|||
GRUB as an intermediate bootloader. See the `Makefile` in the `build32` or
|
||||
`build64` directory for details. The ISO image is both legacy and UEFI
|
||||
bootable, so you need GRUB modules for both legacy and EFI boot installed
|
||||
on your build system. You may need to adjust some path and file names in
|
||||
the make file to match the naming on your system.
|
||||
on your build system (e.g. on Debian, the required GRUB modules are located
|
||||
in packages `grub-pc-bin`, `grub-efi-ia32-bin` and `grub-efi-amd64-bin`).
|
||||
You may need to adjust some path and file names in the Makefile to match
|
||||
the naming on your system.
|
||||
|
||||
The GRUB configuration files contained in the `grub` directory are there for
|
||||
use on the test ISO, but also serve as an example of how to boot Memtest86+
|
||||
|
@ -120,8 +125,12 @@ option consists of an option name, optionally followed by an `=` sign and
|
|||
one or more parameters, separated by commas. The following options are
|
||||
recognised:
|
||||
|
||||
* smp
|
||||
* enables the use of multiple CPU cores at startup
|
||||
* nosmp
|
||||
* disables ACPI table parsing and the use of multiple CPU cores
|
||||
* nobench
|
||||
* disables the integrated memory benchmark
|
||||
* nobigstatus
|
||||
* disables the big PASS/FAIL pop-up status display
|
||||
* nosm
|
||||
* disables SMBUS/SPD parsing, DMI decoding and memory benchmark
|
||||
* nopause
|
||||
|
@ -130,16 +139,35 @@ recognised:
|
|||
* where *type* is one of
|
||||
* legacy
|
||||
* usb
|
||||
* buggy-usb
|
||||
* both
|
||||
* usbdebug
|
||||
* pauses after probing for USB keyboards
|
||||
* usbinit=*mode*
|
||||
* where *mode* is one of
|
||||
* 1 = use the two-step init sequence for high speed devices
|
||||
* 2 = add a second USB reset in the init sequence
|
||||
* 3 = the combination of modes 1 and 2
|
||||
* console=ttyS*x*,*y*
|
||||
* activate serial/tty console output, where *x* is one of the following IO port
|
||||
* 0 = 0x3F8
|
||||
* 1 = 0x2F8
|
||||
* 2 = 0x3E8
|
||||
* 3 = 0x2E8
|
||||
* and *y* is an optional baud rate to choose from the following list
|
||||
* 9600
|
||||
* 19200
|
||||
* 38400
|
||||
* 57600
|
||||
* 115200 (default if not specified or invalid)
|
||||
* 230400
|
||||
|
||||
## Keyboard Selection
|
||||
|
||||
Memtest86+ supports both the legacy keyboard interface (using I/O ports 0x60
|
||||
and 0x64) and USB keyboards (using its own USB device drivers). One or the
|
||||
other can be selected via the boot command line, If neither is selected, the
|
||||
default is to use both. An additional option on the boot command line is
|
||||
`buggy-usb` which enables the longer initialisation sequence required by some
|
||||
older USB devices.
|
||||
other or both can be selected via the boot command line, If not specified on
|
||||
the command line, the default is to use both if the system was booted in UEFI
|
||||
mode, otherwise to only use the legacy interface.
|
||||
|
||||
Older BIOSs usually support USB legacy keyboard emulation, which makes USB
|
||||
keyboards act like legacy keyboards connected to ports 0x60 and 0x64. This
|
||||
|
@ -149,14 +177,20 @@ keyboards directly. The downside of that is that the USB controllers and
|
|||
device drivers require some memory to be reserved for their private use,
|
||||
which means that memory can't then be covered by the memory tests. So to
|
||||
maximise test coverage, if it is supported, enable USB legacy keyboard
|
||||
emulation and add `keyboard=legacy` on the boot command line.
|
||||
emulation and, if booting in UEFI mode, add `keyboard=legacy` on the boot
|
||||
command line.
|
||||
|
||||
**NOTE**: Some UEFI BIOSs only support USB legacy keyboard emulation when
|
||||
you enable the Compatibility System Module (CSM) in the BIOS setup. Others
|
||||
only support it when actually booting in legacy mode.
|
||||
|
||||
**NOTE**: Memtest86+'s USB device drivers are work in progress. Not all USB
|
||||
devices are supported yet, and there may be problems on some hardware.
|
||||
Many USB devices don't fully conform to the USB specification. If the USB
|
||||
keyboard probe hangs or fails to detect your keyboard, try the various
|
||||
workarounds provided by the "usbinit" boot option.
|
||||
|
||||
**NOTE**: Hot-plugging is not currently supported by the Memtest86+ USB
|
||||
drivers. When using these, your USB keyboard should be plugged in before
|
||||
running Memtest86+ and should remain plugged in throughout the test.
|
||||
|
||||
## Operation
|
||||
|
||||
|
@ -170,7 +204,7 @@ At startup, and when running tests, Memtest86+ responds to the following keys:
|
|||
* F1
|
||||
* enters the configuration menu
|
||||
* F2
|
||||
* toggles detection and use of multiple CPU cores (SMP)
|
||||
* toggles use of multiple CPU cores (SMP)
|
||||
* Space
|
||||
* toggles scroll lock (stops/starts error message scrolling)
|
||||
* Enter
|
||||
|
@ -449,8 +483,9 @@ selected by the user.
|
|||
|
||||
### Test 2 : Address test, own address + window
|
||||
|
||||
Across all memory regions, each address is written with its own address plus
|
||||
the window number and then each address is checked for consistency. This
|
||||
Across all memory regions, each address is written with its own virtual
|
||||
address plus the window number (for 32-bit images) or own physical address
|
||||
(for 64-bit images) and then each address is checked for consistency. This
|
||||
catches any errors in the high order address bits that would be missed when
|
||||
testing each window in turn. This test is performed sequentially with each
|
||||
available CPU, regardless of the CPU sequencing mode selected by the user.
|
||||
|
@ -514,12 +549,17 @@ of all zeros and all ones.
|
|||
|
||||
## Known Limitations and Bugs
|
||||
|
||||
* Keyboard may not be detected at launch in UEFI mode
|
||||
* Reboot may not work with some motherboard in UEFI mode
|
||||
* SMP may not be detected on latest Ryzen motherboard
|
||||
Please see the list of [open issues](https://github.com/memtest86plus/memtest86plus/issues)
|
||||
and [enhancement requests](https://github.com/memtest86plus/memtest86plus/discussions)
|
||||
on GitHub.
|
||||
|
||||
Feel free to submit bug reports!
|
||||
|
||||
## Code Contributions
|
||||
|
||||
Code contributions are welcomed, either to fix bugs or to make enhancements.
|
||||
See the README_DEVEL.md in the doc directory for some basic guidelines.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Memtest86+ v6.0 was based on PCMemTest, developed by Martin Whitaker, which
|
||||
|
|
175
app/badram.c
175
app/badram.c
|
@ -34,6 +34,7 @@
|
|||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MAX_PATTERNS 10
|
||||
#define PATTERNS_SIZE (MAX_PATTERNS + 1)
|
||||
|
||||
// DEFAULT_MASK covers a uintptr_t, since that is the testing granularity.
|
||||
#ifdef __x86_64__
|
||||
|
@ -55,14 +56,16 @@ typedef struct {
|
|||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static pattern_t pattern[MAX_PATTERNS];
|
||||
static pattern_t patterns[PATTERNS_SIZE];
|
||||
static int num_patterns = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define COMBINE_MASK(a,b,c,d) ((a & b & c & d) | (~a & b & ~c & d))
|
||||
// New mask is 1 where both masks were 1 (b & d) and the addresses were equal ~(a ^ c).
|
||||
// If addresses were unequal the new mask must be 0 to allow for both values.
|
||||
#define COMBINE_MASK(a,b,c,d) ((b & d) & ~(a ^ c))
|
||||
|
||||
/*
|
||||
* Combine two addr/mask pairs to one addr/mask pair.
|
||||
|
@ -72,7 +75,7 @@ static void combine(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, uintptr_t
|
|||
*mask = COMBINE_MASK(addr1, mask1, addr2, mask2);
|
||||
|
||||
*addr = addr1 | addr2;
|
||||
*addr &= *mask; // Normalise, no fundamental need for this
|
||||
*addr &= *mask; // Normalise to ensure sorting on .addr will work as intended
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -104,58 +107,110 @@ static uintptr_t combi_cost(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, u
|
|||
}
|
||||
|
||||
/*
|
||||
* Find the cheapest array index to extend with the given addr/mask pair.
|
||||
* Return -1 if nothing below the given minimum cost can be found.
|
||||
* Determine if pattern is already covered by an existing pattern.
|
||||
* Return true if that's the case, else false.
|
||||
*/
|
||||
static int cheap_index(uintptr_t addr1, uintptr_t mask1, uintptr_t min_cost)
|
||||
static bool is_covered(pattern_t pattern)
|
||||
{
|
||||
int i = num_patterns;
|
||||
int idx = -1;
|
||||
while (i-- > 0) {
|
||||
uintptr_t tmp_cost = combi_cost(pattern[i].addr, pattern[i].mask, addr1, mask1);
|
||||
if (tmp_cost < min_cost) {
|
||||
for (int i = 0; i < num_patterns; i++) {
|
||||
if (combi_cost(patterns[i].addr, patterns[i].mask, pattern.addr, pattern.mask) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the pair of entries that would be the cheapest to merge.
|
||||
* Assumes patterns is sorted by .addr asc and that for each index i, the cheapest entry to merge with is at i-1 or i+1.
|
||||
* Return -1 if <= 1 patterns exist, else the index of the first entry of the pair (the other being that + 1).
|
||||
*/
|
||||
static int cheapest_pair()
|
||||
{
|
||||
// This is guaranteed to be overwritten with >= 0 as long as num_patterns > 1
|
||||
int merge_idx = -1;
|
||||
|
||||
uintptr_t min_cost = UINTPTR_MAX;
|
||||
for (int i = 0; i < num_patterns - 1; i++) {
|
||||
uintptr_t tmp_cost = combi_cost(
|
||||
patterns[i].addr,
|
||||
patterns[i].mask,
|
||||
patterns[i+1].addr,
|
||||
patterns[i+1].mask
|
||||
);
|
||||
if (tmp_cost <= min_cost) {
|
||||
min_cost = tmp_cost;
|
||||
idx = i;
|
||||
merge_idx = i;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
return merge_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find a relocation index for idx if it costs nothing.
|
||||
* Return -1 if no such index exists.
|
||||
* Remove entries at idx and idx+1.
|
||||
*/
|
||||
static int relocate_index(int idx)
|
||||
static void remove_pair(int idx)
|
||||
{
|
||||
uintptr_t addr = pattern[idx].addr;
|
||||
uintptr_t mask = pattern[idx].mask;
|
||||
pattern[idx].addr = ~pattern[idx].addr; // Never select idx
|
||||
int new = cheap_index(addr, mask, 1 + addresses(mask));
|
||||
pattern[idx].addr = addr;
|
||||
return new;
|
||||
for (int i = idx; i < num_patterns - 2; i++) {
|
||||
patterns[i] = patterns[i + 2];
|
||||
}
|
||||
patterns[num_patterns - 1].addr = 0u;
|
||||
patterns[num_patterns - 1].mask = 0u;
|
||||
patterns[num_patterns - 2].addr = 0u;
|
||||
patterns[num_patterns - 2].mask = 0u;
|
||||
num_patterns -= 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Relocate the given index idx only if free of charge.
|
||||
* This is useful to combine to `neighbouring' sections to integrate.
|
||||
* Inspired on the Buddy memalloc principle in the Linux kernel.
|
||||
* Get the combined entry of idx1 and idx2.
|
||||
*/
|
||||
static void relocate_if_free(int idx)
|
||||
static pattern_t combined_pattern(int idx1, int idx2)
|
||||
{
|
||||
int newidx = relocate_index(idx);
|
||||
if (newidx >= 0) {
|
||||
uintptr_t caddr, cmask;
|
||||
combine(pattern[newidx].addr, pattern[newidx].mask,
|
||||
pattern[ idx].addr, pattern[ idx].mask,
|
||||
&caddr, &cmask);
|
||||
pattern[newidx].addr = caddr;
|
||||
pattern[newidx].mask = cmask;
|
||||
if (idx < --num_patterns) {
|
||||
pattern[idx].addr = pattern[num_patterns].addr;
|
||||
pattern[idx].mask = pattern[num_patterns].mask;
|
||||
pattern_t combined;
|
||||
combine(
|
||||
patterns[idx1].addr,
|
||||
patterns[idx1].mask,
|
||||
patterns[idx2].addr,
|
||||
patterns[idx2].mask,
|
||||
&combined.addr,
|
||||
&combined.mask
|
||||
);
|
||||
return combined;
|
||||
}
|
||||
relocate_if_free (newidx);
|
||||
|
||||
/*
|
||||
* Insert pattern at index idx, shuffling other entries on index towards the end.
|
||||
*/
|
||||
static void insert_at(pattern_t pattern, int idx)
|
||||
{
|
||||
// Move all entries >= idx one index towards the end to make space for the new entry
|
||||
for (int i = num_patterns - 1; i >= idx; i--) {
|
||||
patterns[i + 1] = patterns[i];
|
||||
}
|
||||
|
||||
patterns[idx] = pattern;
|
||||
num_patterns++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert entry (addr, mask) in patterns in an index i so that patterns[i-1].addr < patterns[i]
|
||||
* NOTE: Assumes patterns is already sorted by .addr asc!
|
||||
*/
|
||||
static void insert_sorted(pattern_t pattern)
|
||||
{
|
||||
// Normalise to ensure sorting on .addr will work as intended
|
||||
pattern.addr &= pattern.mask;
|
||||
|
||||
// Find index to insert entry into
|
||||
int new_idx = num_patterns;
|
||||
for (int i = 0; i < num_patterns; i++) {
|
||||
if (pattern.addr < patterns[i].addr) {
|
||||
new_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
insert_at(pattern, new_idx);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -165,26 +220,40 @@ static void relocate_if_free(int idx)
|
|||
void badram_init(void)
|
||||
{
|
||||
num_patterns = 0;
|
||||
|
||||
for (int idx = 0; idx < PATTERNS_SIZE; idx++) {
|
||||
patterns[idx].addr = 0u;
|
||||
patterns[idx].mask = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
bool badram_insert(uintptr_t addr)
|
||||
{
|
||||
if (cheap_index(addr, DEFAULT_MASK, 1) != -1) {
|
||||
pattern_t pattern = {
|
||||
.addr = addr,
|
||||
.mask = DEFAULT_MASK
|
||||
};
|
||||
|
||||
// If covered by existing entry we return immediately
|
||||
if (is_covered(pattern)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num_patterns < MAX_PATTERNS) {
|
||||
pattern[num_patterns].addr = addr;
|
||||
pattern[num_patterns].mask = DEFAULT_MASK;
|
||||
num_patterns++;
|
||||
relocate_if_free(num_patterns - 1);
|
||||
} else {
|
||||
int idx = cheap_index(addr, DEFAULT_MASK, UINTPTR_MAX);
|
||||
uintptr_t caddr, cmask;
|
||||
combine(pattern[idx].addr, pattern[idx].mask, addr, DEFAULT_MASK, &caddr, &cmask);
|
||||
pattern[idx].addr = caddr;
|
||||
pattern[idx].mask = cmask;
|
||||
relocate_if_free(idx);
|
||||
// Add entry in order sorted by .addr asc
|
||||
insert_sorted(pattern);
|
||||
|
||||
// If we have more patterns than the max we need to force a merge
|
||||
if (num_patterns > MAX_PATTERNS) {
|
||||
// Find the pair that is the cheapest to merge
|
||||
// merge_idx will be -1 if num_patterns < 2, but that means MAX_PATTERNS = 0 which is not a valid state anyway
|
||||
int merge_idx = cheapest_pair();
|
||||
|
||||
pattern_t combined = combined_pattern(merge_idx, merge_idx + 1);
|
||||
|
||||
// Remove the source pair so that we can maintain order as combined does not necessarily belong in merge_idx
|
||||
remove_pair(merge_idx);
|
||||
|
||||
insert_sorted(combined);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -214,8 +283,8 @@ void badram_display(void)
|
|||
col = 7;
|
||||
}
|
||||
display_scrolled_message(col, "0x%0*x,0x%0*x",
|
||||
TESTWORD_DIGITS, pattern[i].addr,
|
||||
TESTWORD_DIGITS, pattern[i].mask);
|
||||
TESTWORD_DIGITS, patterns[i].addr,
|
||||
TESTWORD_DIGITS, patterns[i].mask);
|
||||
col += text_width;
|
||||
}
|
||||
}
|
||||
|
|
144
app/config.c
144
app/config.c
|
@ -1,11 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
//
|
||||
// Derived from memtest86+ config.c:
|
||||
//
|
||||
// MemTest86+ V5.00 Specific code (GPL V2.0)
|
||||
// By Samuel DEMEULEMEESTER, sdemeule@memtest.org
|
||||
// http://www.x86-secret.com - http://www.memtest.org
|
||||
// ----------------------------------------------------
|
||||
// config.c - MemTest-86 Version 3.4
|
||||
//
|
||||
|
@ -19,6 +17,7 @@
|
|||
#include "bootparams.h"
|
||||
|
||||
#include "cpuinfo.h"
|
||||
#include "cpuid.h"
|
||||
#include "hwctrl.h"
|
||||
#include "keyboard.h"
|
||||
#include "memsize.h"
|
||||
|
@ -87,8 +86,12 @@ error_mode_t error_mode = ERROR_MODE_NONE;
|
|||
|
||||
cpu_state_t cpu_state[MAX_CPUS];
|
||||
|
||||
core_type_t hybrid_core_type[MAX_CPUS];
|
||||
bool exclude_ecores = true;
|
||||
|
||||
bool smp_enabled = true;
|
||||
|
||||
bool enable_big_status = true;
|
||||
bool enable_temperature = true;
|
||||
bool enable_trace = false;
|
||||
|
||||
|
@ -104,71 +107,114 @@ int tty_params_port = SERIAL_PORT_0x3F8;
|
|||
int tty_params_baud = SERIAL_DEFAULT_BAUDRATE;
|
||||
int tty_update_period = 2; // Update TTY every 2 seconds (default)
|
||||
|
||||
bool err_banner_redraw = false; // Redraw banner on new errors (if previsouly removed)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void parse_serial_params(const char *params)
|
||||
{
|
||||
enable_tty = true;
|
||||
|
||||
// No parameters passed (only "console"), use default
|
||||
if (params == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No TTY port passed, use default ttyS0
|
||||
if (strncmp(params, "ttyS", 5) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure TTY port or use default
|
||||
if (params[4] >= '0' && params[4] <= '3') {
|
||||
tty_params_port = params[4] - '0';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
enable_tty = true;
|
||||
|
||||
if (params[5] != ',' && params[5] != ' ') {
|
||||
// No Baud Rate specified, use default
|
||||
if (params[5] != ',' || params[6] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[6] >= '0' && params[6] <= '9') {
|
||||
|
||||
switch (params[6])
|
||||
{
|
||||
default:
|
||||
return;
|
||||
case '0':
|
||||
tty_params_baud = 9600;
|
||||
tty_update_period = 5;
|
||||
break;
|
||||
case '1':
|
||||
tty_params_baud = 19200;
|
||||
tty_update_period = 4;
|
||||
tty_params_baud = (params[7] == '9') ? 19200 : 115200;
|
||||
tty_update_period = (params[7] == '9') ? 4 : 2;
|
||||
break;
|
||||
case '2':
|
||||
tty_params_baud = 38400;
|
||||
tty_update_period = 3;
|
||||
tty_params_baud = 230400;
|
||||
tty_update_period = 2;
|
||||
break;
|
||||
case '3':
|
||||
tty_params_baud = 38400;
|
||||
tty_update_period = 4;
|
||||
break;
|
||||
case '5':
|
||||
tty_params_baud = 57600;
|
||||
tty_update_period = 3;
|
||||
break;
|
||||
case '4':
|
||||
tty_params_baud = 115200;
|
||||
tty_update_period = 2;
|
||||
case '7':
|
||||
tty_params_baud = 76800;
|
||||
tty_update_period = 3;
|
||||
break;
|
||||
case '9':
|
||||
tty_params_baud = 9600;
|
||||
tty_update_period = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void parse_option(const char *option, const char *params)
|
||||
{
|
||||
if (option[0] == '\0') return;
|
||||
|
||||
if (strncmp(option, "keyboard", 9) == 0 && params != NULL) {
|
||||
if (strncmp(option, "console", 8) == 0) {
|
||||
parse_serial_params(params);
|
||||
} else if (strncmp(option, "cpuseqmode", 11) == 0) {
|
||||
if (strncmp(params, "par", 4) == 0) {
|
||||
cpu_mode = PAR;
|
||||
} else if (strncmp(params, "seq", 4) == 0) {
|
||||
cpu_mode = SEQ;
|
||||
} else if (strncmp(params, "rr", 3) == 0 || strncmp(params, "one", 4) == 0) {
|
||||
cpu_mode = ONE;
|
||||
}
|
||||
} else if (strncmp(option, "reportmode", 11) == 0) {
|
||||
if (strncmp(params, "none", 5) == 0) {
|
||||
error_mode = ERROR_MODE_NONE;
|
||||
} else if (strncmp(params, "summary", 8) == 0) {
|
||||
error_mode = ERROR_MODE_SUMMARY;
|
||||
} else if (strncmp(params, "address", 8) == 0) {
|
||||
error_mode = ERROR_MODE_ADDRESS;
|
||||
} else if (strncmp(params, "badram", 7) == 0) {
|
||||
error_mode = ERROR_MODE_BADRAM;
|
||||
}
|
||||
} else if (strncmp(option, "keyboard", 9) == 0 && params != NULL) {
|
||||
if (strncmp(params, "legacy", 7) == 0) {
|
||||
keyboard_types = KT_LEGACY;
|
||||
} else if (strncmp(params, "usb", 4) == 0) {
|
||||
keyboard_types = KT_USB;
|
||||
} else if (strncmp(params, "buggy-usb", 10) == 0) {
|
||||
keyboard_types = KT_USB;
|
||||
usb_init_options |= USB_EXTRA_RESET;
|
||||
} else if (strncmp(params, "both", 5) == 0) {
|
||||
keyboard_types = KT_USB|KT_LEGACY;
|
||||
}
|
||||
} else if (strncmp(option, "nobench", 8) == 0) {
|
||||
enable_bench = false;
|
||||
} else if (strncmp(option, "nobigstatus", 12) == 0) {
|
||||
enable_big_status = false;
|
||||
} else if (strncmp(option, "noehci", 7) == 0) {
|
||||
usb_init_options |= USB_IGNORE_EHCI;
|
||||
} else if (strncmp(option, "nopause", 8) == 0) {
|
||||
pause_at_start = false;
|
||||
} else if (strncmp(option, "nosm", 5) == 0) {
|
||||
enable_sm = false;
|
||||
} else if (strncmp(option, "nosmp", 6) == 0) {
|
||||
smp_enabled = false;
|
||||
} else if (strncmp(option, "powersave", 10) == 0) {
|
||||
if (strncmp(params, "off", 4) == 0) {
|
||||
power_save = POWER_SAVE_OFF;
|
||||
|
@ -177,24 +223,18 @@ static void parse_option(const char *option, const char *params)
|
|||
} else if (strncmp(params, "high", 5) == 0) {
|
||||
power_save = POWER_SAVE_HIGH;
|
||||
}
|
||||
} else if (strncmp(option, "console", 8) == 0) {
|
||||
if (params != NULL) {
|
||||
parse_serial_params(params);
|
||||
}
|
||||
} else if (strncmp(option, "nobench", 8) == 0) {
|
||||
enable_bench = false;
|
||||
} else if (strncmp(option, "noehci", 7) == 0) {
|
||||
usb_init_options |= USB_IGNORE_EHCI;
|
||||
} else if (strncmp(option, "nopause", 8) == 0) {
|
||||
pause_at_start = false;
|
||||
} else if (strncmp(option, "smp", 4) == 0) {
|
||||
smp_enabled = true;
|
||||
} else if (strncmp(option, "trace", 6) == 0) {
|
||||
enable_trace = true;
|
||||
} else if (strncmp(option, "usbdebug", 9) == 0) {
|
||||
usb_init_options |= USB_DEBUG;
|
||||
} else if (strncmp(option, "nosm", 5) == 0) {
|
||||
enable_sm = false;
|
||||
} else if (strncmp(option, "usbinit", 8) == 0) {
|
||||
if (strncmp(params, "1", 2) == 0) {
|
||||
usb_init_options |= USB_2_STEP_INIT;
|
||||
} else if (strncmp(params, "2", 2) == 0) {
|
||||
usb_init_options |= USB_EXTRA_RESET;
|
||||
} else if (strncmp(params, "3", 2) == 0) {
|
||||
usb_init_options |= USB_2_STEP_INIT|USB_EXTRA_RESET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,7 +731,14 @@ static void cpu_selection_menu(void)
|
|||
prints(POP_R+5, POP_LI, "<F3> Add one CPU");
|
||||
prints(POP_R+6, POP_LI, "<F4> Add CPU range");
|
||||
prints(POP_R+7, POP_LI, "<F5> Add all CPUs");
|
||||
prints(POP_R+8, POP_LI, "<F10> Exit menu");
|
||||
if (cpuid_info.topology.is_hybrid) {
|
||||
if (exclude_ecores) {
|
||||
prints(POP_R+8, POP_LI, "<F6> Include E-Cores");
|
||||
} else {
|
||||
prints(POP_R+8, POP_LI, "<F6> Exclude E-Cores");
|
||||
}
|
||||
}
|
||||
prints(POP_R+9, POP_LI, "<F10> Exit menu");
|
||||
|
||||
display_cpu_selection(display_offset);
|
||||
|
||||
|
@ -714,6 +761,10 @@ static void cpu_selection_menu(void)
|
|||
case '5':
|
||||
changed = set_all_cpus(true, display_offset);
|
||||
break;
|
||||
case '6':
|
||||
exclude_ecores = !exclude_ecores;
|
||||
prints(POP_R+8, POP_LI+6, exclude_ecores ? "Exclude" : "Include");
|
||||
break;
|
||||
case 'u':
|
||||
if (display_offset >= SEL_W) {
|
||||
display_offset -= SEL_W;
|
||||
|
@ -795,9 +846,9 @@ void config_menu(bool initial)
|
|||
prints(POP_R+5, POP_LI, "<F3> CPU sequencing mode");
|
||||
prints(POP_R+6, POP_LI, "<F4> Error reporting mode");
|
||||
if (initial) {
|
||||
if (num_available_cpus < 2) set_foreground_colour(BOLD+BLACK);
|
||||
if (!smp_enabled) set_foreground_colour(BOLD+BLACK);
|
||||
prints(POP_R+7, POP_LI, "<F5> CPU selection");
|
||||
if (num_available_cpus < 2) set_foreground_colour(WHITE);
|
||||
if (!smp_enabled) set_foreground_colour(WHITE);
|
||||
if (no_temperature) set_foreground_colour(BOLD+BLACK);
|
||||
printf(POP_R+8, POP_LI, "<F6> Temperature %s", enable_temperature ? "disable" : "enable ");
|
||||
if (no_temperature) set_foreground_colour(WHITE);
|
||||
|
@ -829,7 +880,7 @@ void config_menu(bool initial)
|
|||
break;
|
||||
case '5':
|
||||
if (initial) {
|
||||
if (num_available_cpus > 1) {
|
||||
if (smp_enabled) {
|
||||
cpu_selection_menu();
|
||||
}
|
||||
} else {
|
||||
|
@ -880,7 +931,9 @@ void initial_config(void)
|
|||
{
|
||||
display_initial_notice();
|
||||
|
||||
bool smp_init_done = false;
|
||||
if (num_available_cpus < 2) {
|
||||
smp_enabled = false;
|
||||
}
|
||||
if (pause_at_start) {
|
||||
bool got_key = false;
|
||||
for (int i = 0; i < 3000 && !got_key; i++) {
|
||||
|
@ -892,8 +945,6 @@ void initial_config(void)
|
|||
reboot();
|
||||
break;
|
||||
case '1':
|
||||
smp_init(smp_enabled);
|
||||
smp_init_done = true;
|
||||
config_menu(true);
|
||||
got_key = true;
|
||||
break;
|
||||
|
@ -913,7 +964,4 @@ void initial_config(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!smp_init_done) {
|
||||
smp_init(smp_enabled);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "smp.h"
|
||||
#include "cpuid.h"
|
||||
|
||||
typedef enum {
|
||||
PAR,
|
||||
|
@ -45,8 +46,12 @@ extern error_mode_t error_mode;
|
|||
|
||||
extern cpu_state_t cpu_state[MAX_CPUS];
|
||||
|
||||
extern core_type_t hybrid_core_type[MAX_CPUS];
|
||||
extern bool exclude_ecores;
|
||||
|
||||
extern bool smp_enabled;
|
||||
|
||||
extern bool enable_big_status;
|
||||
extern bool enable_temperature;
|
||||
extern bool enable_trace;
|
||||
|
||||
|
@ -62,6 +67,8 @@ extern int tty_params_port;
|
|||
extern int tty_params_baud;
|
||||
extern int tty_update_period;
|
||||
|
||||
extern bool err_banner_redraw;
|
||||
|
||||
void config_init(void);
|
||||
|
||||
void config_menu(bool initial);
|
||||
|
|
177
app/display.c
177
app/display.c
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
@ -21,7 +22,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "githash.h"
|
||||
#include "build_version.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
|
@ -31,13 +32,24 @@
|
|||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define POP_STAT_R 12
|
||||
#define POP_STAT_C 18
|
||||
|
||||
#define POP_STAT_W 44
|
||||
#define POP_STAT_H 11
|
||||
|
||||
#define POP_STAT_LAST_R (POP_STAT_R + POP_STAT_H - 1)
|
||||
#define POP_STAT_LAST_C (POP_STAT_C + POP_STAT_W - 1)
|
||||
|
||||
#define POP_STATUS_REGION POP_STAT_R, POP_STAT_C, POP_STAT_LAST_R, POP_STAT_LAST_C
|
||||
|
||||
#define SPINNER_PERIOD 100 // milliseconds
|
||||
|
||||
#define NUM_SPIN_STATES 4
|
||||
|
||||
static const char spin_state[NUM_SPIN_STATES] = { '|', '/', '-', '\\' };
|
||||
|
||||
static const char *cpu_mode_str[] = { "PAR", "SEQ", "RR " };
|
||||
static const char cpu_mode_str[3][4] = { "PAR", "SEQ", "RR " };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
|
@ -60,6 +72,9 @@ static uint64_t next_spin_time = 0; // TSC time stamp
|
|||
static int prev_sec = -1; // previous second
|
||||
static bool timed_update_done = false; // update cycle status
|
||||
|
||||
bool big_status_displayed = false;
|
||||
static uint16_t popup_status_save_buffer[POP_STAT_W * POP_STAT_H];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -80,28 +95,29 @@ void display_init(void)
|
|||
|
||||
clear_screen();
|
||||
|
||||
/* The commented horizontal lines provide visual cue for where and how
|
||||
* they will appear on the screen. They are drawn down below using
|
||||
* Extended ASCII characters.
|
||||
*/
|
||||
|
||||
set_foreground_colour(BLACK);
|
||||
set_background_colour(WHITE);
|
||||
clear_screen_region(0, 0, 0, 27);
|
||||
prints(0, 0, " Memtest86+ v6.00b1");
|
||||
prints(0, 0, " Memtest86+ v" MT_VERSION);
|
||||
set_foreground_colour(RED);
|
||||
printc(0, 14, '+');
|
||||
printc(0, 15, '+');
|
||||
set_foreground_colour(WHITE);
|
||||
set_background_colour(BLUE);
|
||||
prints(0,28, "| ");
|
||||
prints(1, 0, "CLK/Temp: N/A | Pass %");
|
||||
prints(2, 0, "L1 Cache: N/A | Test %");
|
||||
prints(3, 0, "L2 Cache: N/A | Test #");
|
||||
prints(4, 0, "L3 Cache: N/A | Testing:");
|
||||
prints(5, 0, "Memory : N/A | Pattern:");
|
||||
prints(6, 0, "--------------------------------------------------------------------------------");
|
||||
// prints(6, 0, "--------------------------------------------------------------------------------");
|
||||
prints(7, 0, "CPU: SMP: N/A | Time: Status: Init.");
|
||||
prints(8, 0, "Using: | Pass: Errors:");
|
||||
prints(9, 0, "--------------------------------------------------------------------------------");
|
||||
// prints(9, 0, "--------------------------------------------------------------------------------");
|
||||
|
||||
// Redraw lines using box drawing characters.
|
||||
// Disable if TTY is enabled to avoid VT100 char replacements
|
||||
if (!enable_tty) {
|
||||
for (int i = 0;i < 80; i++) {
|
||||
print_char(6, i, 0xc4);
|
||||
print_char(9, i, 0xc4);
|
||||
|
@ -112,17 +128,16 @@ void display_init(void)
|
|||
for (int i = 7; i < 10; i++) {
|
||||
print_char(i, 42, 0xb3);
|
||||
}
|
||||
|
||||
print_char(6, 28, 0xc1);
|
||||
print_char(6, 42, 0xc2);
|
||||
print_char(9, 42, 0xc1);
|
||||
}
|
||||
|
||||
set_foreground_colour(BLUE);
|
||||
set_background_colour(WHITE);
|
||||
clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1);
|
||||
prints(ROW_FOOTER, 0, " <ESC> Exit <F1> Configuration <Space> Scroll Lock");
|
||||
prints(ROW_FOOTER, 64, "6.00.");
|
||||
prints(ROW_FOOTER, 69, GIT_HASH);
|
||||
prints(ROW_FOOTER, 64, MT_VERSION "." GIT_HASH);
|
||||
#if TESTWORD_WIDTH > 32
|
||||
prints(ROW_FOOTER, 76, ".x64");
|
||||
#else
|
||||
|
@ -178,8 +193,13 @@ void display_cpu_topology(void)
|
|||
extern int num_enabled_cpus;
|
||||
int num_cpu_sockets = 1;
|
||||
|
||||
// Display Thread Count and Thread Dispatch Mode
|
||||
if (smp_enabled) {
|
||||
if (cpuid_info.topology.is_hybrid && cpuid_info.topology.ecore_count > 0 && exclude_ecores) {
|
||||
display_threading(num_enabled_cpus - cpuid_info.topology.ecore_count, cpu_mode_str[cpu_mode]);
|
||||
} else {
|
||||
display_threading(num_enabled_cpus, cpu_mode_str[cpu_mode]);
|
||||
}
|
||||
} else {
|
||||
display_threading_disabled();
|
||||
}
|
||||
|
@ -198,15 +218,24 @@ void display_cpu_topology(void)
|
|||
// Compute number of sockets according to individual CPU core count
|
||||
if (num_enabled_cpus > cpuid_info.topology.thread_count &&
|
||||
num_enabled_cpus % cpuid_info.topology.thread_count == 0) {
|
||||
|
||||
num_cpu_sockets = num_enabled_cpus / cpuid_info.topology.thread_count;
|
||||
}
|
||||
|
||||
|
||||
// Temporary workaround for Hybrid CPUs.
|
||||
// TODO: run cpuid on each core to get correct P+E topology
|
||||
// Display P/E-Core count for Hybrid CPUs.
|
||||
if (cpuid_info.topology.is_hybrid) {
|
||||
display_cpu_topo_hybrid(cpuid_info.topology.thread_count);
|
||||
if (cpuid_info.topology.pcore_count > 1) {
|
||||
|
||||
if (cpuid_info.flags.htt &&
|
||||
(cpuid_info.topology.thread_count - cpuid_info.topology.ecore_count) == cpuid_info.topology.pcore_count) {
|
||||
cpuid_info.topology.pcore_count /= 2;
|
||||
}
|
||||
|
||||
display_cpu_topo_hybrid(cpuid_info.topology.pcore_count,
|
||||
cpuid_info.topology.ecore_count,
|
||||
cpuid_info.topology.thread_count);
|
||||
} else {
|
||||
display_cpu_topo_hybrid_short(cpuid_info.topology.thread_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -236,12 +265,16 @@ void post_display_init(void)
|
|||
if (false) {
|
||||
// Try to get RAM information from IMC (TODO)
|
||||
display_spec_mode("IMC: ");
|
||||
display_spec(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
|
||||
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS);
|
||||
display_mode = DISPLAY_MODE_IMC;
|
||||
} else if (ram.freq > 0 && ram.tCL > 1) {
|
||||
} else if (ram.freq > 0 && ram.tCL > 0) {
|
||||
// If not available, grab max memory specs from SPD
|
||||
display_spec_mode("RAM: ");
|
||||
display_spec(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
|
||||
if (ram.freq <= 166) {
|
||||
display_spec_sdr(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
|
||||
} else {
|
||||
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS);
|
||||
}
|
||||
display_mode = DISPLAY_MODE_SPD;
|
||||
} else {
|
||||
// If nothing available, fallback to "Using Core" Display
|
||||
|
@ -294,9 +327,92 @@ void display_start_test(void)
|
|||
test_ticks = 0;
|
||||
}
|
||||
|
||||
void display_temperature(void)
|
||||
{
|
||||
if (!enable_temperature) {
|
||||
return;
|
||||
}
|
||||
|
||||
int actual_cpu_temp = get_cpu_temperature();
|
||||
|
||||
if (actual_cpu_temp == 0) {
|
||||
if (max_cpu_temp == 0) {
|
||||
enable_temperature = false;
|
||||
no_temperature = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_cpu_temp < actual_cpu_temp ) {
|
||||
max_cpu_temp = actual_cpu_temp;
|
||||
}
|
||||
|
||||
int offset = actual_cpu_temp / 100 + max_cpu_temp / 100;
|
||||
|
||||
clear_screen_region(1, 18, 1, 22);
|
||||
printf(1, 20-offset, "%2i/%2i%cC", actual_cpu_temp, max_cpu_temp, 0xF8);
|
||||
}
|
||||
|
||||
void display_big_status(bool pass)
|
||||
{
|
||||
if (!enable_big_status || big_status_displayed) {
|
||||
return;
|
||||
}
|
||||
|
||||
save_screen_region(POP_STATUS_REGION, popup_status_save_buffer);
|
||||
|
||||
set_background_colour(BLACK);
|
||||
set_foreground_colour(pass ? GREEN : RED);
|
||||
clear_screen_region(POP_STATUS_REGION);
|
||||
|
||||
if (pass) {
|
||||
prints(POP_STAT_R+1, POP_STAT_C+5, "###### ## ##### ##### ");
|
||||
prints(POP_STAT_R+2, POP_STAT_C+5, "## ## #### ## ## ## ## ");
|
||||
prints(POP_STAT_R+3, POP_STAT_C+5, "## ## ## ## ## ## ");
|
||||
prints(POP_STAT_R+4, POP_STAT_C+5, "###### ## ## ##### ##### ");
|
||||
prints(POP_STAT_R+5, POP_STAT_C+5, "## ######## ## ## ");
|
||||
prints(POP_STAT_R+6, POP_STAT_C+5, "## ## ## ## ## ## ## ");
|
||||
prints(POP_STAT_R+7, POP_STAT_C+5, "## ## ## ##### ##### ");
|
||||
} else {
|
||||
prints(POP_STAT_R+1, POP_STAT_C+5, "####### ## ###### ## ");
|
||||
prints(POP_STAT_R+2, POP_STAT_C+5, "## #### ## ## ");
|
||||
prints(POP_STAT_R+3, POP_STAT_C+5, "## ## ## ## ## ");
|
||||
prints(POP_STAT_R+4, POP_STAT_C+5, "##### ## ## ## ## ");
|
||||
prints(POP_STAT_R+5, POP_STAT_C+5, "## ######## ## ## ");
|
||||
prints(POP_STAT_R+6, POP_STAT_C+5, "## ## ## ## ## ");
|
||||
prints(POP_STAT_R+7, POP_STAT_C+5, "## ## ## ###### ###### ");
|
||||
}
|
||||
|
||||
prints(POP_STAT_R+8, POP_STAT_C+5, " ");
|
||||
prints(POP_STAT_R+9, POP_STAT_C+5, "Press any key to remove this banner ");
|
||||
|
||||
set_background_colour(BLUE);
|
||||
set_foreground_colour(WHITE);
|
||||
big_status_displayed = true;
|
||||
}
|
||||
|
||||
void restore_big_status(void)
|
||||
{
|
||||
if (!big_status_displayed) {
|
||||
return;
|
||||
}
|
||||
|
||||
restore_screen_region(POP_STATUS_REGION, popup_status_save_buffer);
|
||||
big_status_displayed = false;
|
||||
}
|
||||
|
||||
void check_input(void)
|
||||
{
|
||||
switch (get_key()) {
|
||||
char input_key = get_key();
|
||||
|
||||
if (input_key == '\0') {
|
||||
return;
|
||||
} else if (big_status_displayed) {
|
||||
restore_big_status();
|
||||
enable_big_status = false;
|
||||
}
|
||||
|
||||
switch (input_key) {
|
||||
case ESC:
|
||||
clear_message_area();
|
||||
display_notice("Rebooting...");
|
||||
|
@ -401,7 +517,7 @@ void do_tick(int my_cpu)
|
|||
if (clks_per_msec > 0) {
|
||||
uint64_t current_time = get_tsc();
|
||||
|
||||
int secs = (current_time - run_start_time) / (1000 * clks_per_msec);
|
||||
int secs = (current_time - run_start_time) / (1000 * (uint64_t)clks_per_msec);
|
||||
int mins = secs / 60; secs %= 60; act_sec = secs;
|
||||
int hours = mins / 60; mins %= 60;
|
||||
display_run_time(hours, mins, secs);
|
||||
|
@ -426,18 +542,13 @@ void do_tick(int my_cpu)
|
|||
// This only tick one time per second
|
||||
if (!timed_update_done) {
|
||||
|
||||
// Update temperature one time per second
|
||||
if (enable_temperature) {
|
||||
int actual_cpu_temp = get_cpu_temperature();
|
||||
|
||||
if(max_cpu_temp < actual_cpu_temp ) {
|
||||
max_cpu_temp = actual_cpu_temp;
|
||||
// Display FAIL banner if (new) errors detected
|
||||
if (err_banner_redraw && !big_status_displayed && error_count > 1) {
|
||||
display_big_status(false);
|
||||
}
|
||||
|
||||
if(actual_cpu_temp != 0) {
|
||||
display_temperature(actual_cpu_temp, max_cpu_temp);
|
||||
}
|
||||
}
|
||||
// Update temperature
|
||||
display_temperature();
|
||||
|
||||
// Update TTY one time every TTY_UPDATE_PERIOD second(s)
|
||||
if (enable_tty) {
|
||||
|
|
|
@ -81,7 +81,13 @@ typedef enum {
|
|||
#define display_threading_disabled() \
|
||||
prints(7,31, "Disabled")
|
||||
|
||||
#define display_cpu_topo_hybrid(num_threads) \
|
||||
#define display_cpu_topo_hybrid(num_pcores, num_ecores, num_threads) \
|
||||
{ \
|
||||
clear_screen_region(7, 5, 7, 25); \
|
||||
printf(7, 5, "%uP+%uE-Cores (%uT)", num_pcores, num_ecores, num_threads); \
|
||||
}
|
||||
|
||||
#define display_cpu_topo_hybrid_short(num_threads) \
|
||||
printf(7, 5, "%u Threads (Hybrid)", num_threads)
|
||||
|
||||
#define display_cpu_topo_multi_socket(num_sockets, num_cores, num_threads) \
|
||||
|
@ -96,9 +102,13 @@ typedef enum {
|
|||
#define display_spec_mode(mode) \
|
||||
prints(8,0, mode);
|
||||
|
||||
#define display_spec(freq, type, cl, rcd, rp, ras) \
|
||||
printf(8,5, "%uMHz (%s-%u) CAS %u-%u-%u-%u", \
|
||||
freq / 2, type, freq, cl, rcd, rp, ras);
|
||||
#define display_spec_ddr(freq, type, cl, cl_dec, rcd, rp, ras) \
|
||||
printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \
|
||||
freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras);
|
||||
|
||||
#define display_spec_sdr(freq, type, cl, rcd, rp, ras) \
|
||||
printf(8,5, "%uMHz (%s PC%u) CAS %u-%u-%u-%u", \
|
||||
freq, type, freq, cl, rcd, rp, ras);
|
||||
|
||||
#define display_dmi_mb(sys_ma, sys_sku) \
|
||||
dmicol = prints(23, dmicol, sys_man); \
|
||||
|
@ -171,9 +181,6 @@ typedef enum {
|
|||
#define display_run_time(hours, mins, secs) \
|
||||
printf(7, 51, "%i:%02i:%02i", hours, mins, secs)
|
||||
|
||||
#define display_temperature(temp, maxtemp) \
|
||||
printf(1, 20, "%2i/%2i%cC", temp, maxtemp, 0xf8)
|
||||
|
||||
#define display_pass_count(count) \
|
||||
printi(8, 51, count, 0, false, true)
|
||||
|
||||
|
@ -193,10 +200,10 @@ typedef enum {
|
|||
printf(scroll_message_row, col, __VA_ARGS__)
|
||||
|
||||
#define display_notice(str) \
|
||||
prints(ROW_MESSAGE_T + 6, (SCREEN_WIDTH - strlen(str)) / 2, str)
|
||||
prints(ROW_MESSAGE_T + 8, (SCREEN_WIDTH - strlen(str)) / 2, str)
|
||||
|
||||
#define display_notice_with_args(length, ...) \
|
||||
printf(ROW_MESSAGE_T + 6, (SCREEN_WIDTH - length) / 2, __VA_ARGS__)
|
||||
printf(ROW_MESSAGE_T + 8, (SCREEN_WIDTH - length) / 2, __VA_ARGS__)
|
||||
|
||||
#define clear_footer_message() \
|
||||
{ \
|
||||
|
@ -231,6 +238,12 @@ void display_start_pass(void);
|
|||
|
||||
void display_start_test(void);
|
||||
|
||||
void display_temperature(void);
|
||||
|
||||
void display_big_status(bool pass);
|
||||
|
||||
void restore_big_status(void);
|
||||
|
||||
void check_input(void);
|
||||
|
||||
void set_scroll_lock(bool enabled);
|
||||
|
|
|
@ -151,6 +151,8 @@ static void common_err(error_type_t type, uintptr_t addr, testword_t good, testw
|
|||
{
|
||||
spin_lock(error_mutex);
|
||||
|
||||
restore_big_status();
|
||||
|
||||
bool new_header = (error_count == 0) || (error_mode != last_error_mode);
|
||||
if (new_header) {
|
||||
clear_message_area();
|
||||
|
@ -334,7 +336,7 @@ void addr_error(testword_t *addr1, testword_t *addr2, testword_t good, testword_
|
|||
void data_error(testword_t *addr, testword_t good, testword_t bad, bool use_for_badram)
|
||||
{
|
||||
#if USB_WORKAROUND
|
||||
/* Skip any errrors that appear to be due to the BIOS using location
|
||||
/* Skip any errors that appear to be due to the BIOS using location
|
||||
* 0x4e0 for USB keyboard support. This often happens with Intel
|
||||
* 810, 815 and 820 chipsets. It is possible that we will skip
|
||||
* a real error but the odds are very low.
|
||||
|
@ -369,6 +371,11 @@ void error_update(void)
|
|||
display_error_count(error_count);
|
||||
display_status("Failed!");
|
||||
|
||||
// Display FAIL banner on first error
|
||||
if (error_count == 1) {
|
||||
display_big_status(false);
|
||||
}
|
||||
|
||||
if (enable_tty) {
|
||||
tty_error_redraw();
|
||||
}
|
||||
|
|
90
app/main.c
90
app/main.c
|
@ -4,8 +4,8 @@
|
|||
// Derived from memtest86+ main.c:
|
||||
//
|
||||
// MemTest86+ V5 Specific code (GPL V2.0)
|
||||
// By Samuel DEMEULEMEESTER, sdemeule@memtest.org
|
||||
// http://www.canardpc.com - http://www.memtest.org
|
||||
// By Samuel DEMEULEMEESTER, memtest@memtest.org
|
||||
// https://www.memtest.org
|
||||
// ------------------------------------------------
|
||||
// main.c - MemTest-86 Version 3.5
|
||||
//
|
||||
|
@ -18,10 +18,13 @@
|
|||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
|
||||
#include "acpi.h"
|
||||
#include "cache.h"
|
||||
#include "cpuid.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "heap.h"
|
||||
#include "hwctrl.h"
|
||||
#include "hwquirks.h"
|
||||
#include "io.h"
|
||||
#include "keyboard.h"
|
||||
#include "pmem.h"
|
||||
|
@ -32,6 +35,7 @@
|
|||
#include "smbios.h"
|
||||
#include "smp.h"
|
||||
#include "temperature.h"
|
||||
#include "timers.h"
|
||||
#include "vmem.h"
|
||||
|
||||
#include "unistd.h"
|
||||
|
@ -197,26 +201,33 @@ static bool set_load_addr(uintptr_t *load_addr, size_t program_size, uintptr_t l
|
|||
|
||||
static void global_init(void)
|
||||
{
|
||||
// This is the first region we map, so is guaranteed not to fail.
|
||||
boot_params_addr = map_region(boot_params_addr, sizeof(boot_params_t), true);
|
||||
|
||||
floppy_off();
|
||||
|
||||
hwctrl_init();
|
||||
|
||||
cpuid_init();
|
||||
|
||||
// Nothing before this should access the boot parameters, in case they are located above 4GB.
|
||||
// This is the first region we map, so it is guaranteed not to fail.
|
||||
boot_params_addr = map_region(boot_params_addr, sizeof(boot_params_t), true);
|
||||
|
||||
hwctrl_init();
|
||||
|
||||
screen_init();
|
||||
|
||||
cpuinfo_init();
|
||||
|
||||
pmem_init();
|
||||
|
||||
heap_init();
|
||||
|
||||
pci_init();
|
||||
|
||||
membw_init();
|
||||
quirks_init();
|
||||
|
||||
tty_init();
|
||||
acpi_init();
|
||||
|
||||
timers_init();
|
||||
|
||||
membw_init();
|
||||
|
||||
smbios_init();
|
||||
|
||||
|
@ -224,16 +235,32 @@ static void global_init(void)
|
|||
|
||||
config_init();
|
||||
|
||||
tty_init();
|
||||
|
||||
smp_init(smp_enabled);
|
||||
|
||||
// At this point we have started reserving physical pages in the memory
|
||||
// map for data structures that need to be permanently pinned in place.
|
||||
// This may overwrite any data structures passed to us by the BIOS and/or
|
||||
// boot loader, e.g. the boot parameters, boot command line, and ACPI
|
||||
// tables. So do not access those data structures after this point.
|
||||
|
||||
keyboard_init();
|
||||
|
||||
display_init();
|
||||
|
||||
error_init();
|
||||
|
||||
temperature_init();
|
||||
|
||||
initial_config();
|
||||
|
||||
clear_message_area();
|
||||
|
||||
if (!smp_enabled) {
|
||||
num_available_cpus = 1;
|
||||
}
|
||||
|
||||
num_enabled_cpus = 0;
|
||||
for (int i = 0; i < num_available_cpus; i++) {
|
||||
if (cpu_state[i] == CPU_STATE_ENABLED) {
|
||||
|
@ -245,15 +272,8 @@ static void global_init(void)
|
|||
|
||||
master_cpu = 0;
|
||||
|
||||
if (enable_temperature) {
|
||||
int temp = get_cpu_temperature();
|
||||
if (temp > 0) {
|
||||
display_temperature(temp, temp);
|
||||
} else {
|
||||
enable_temperature = false;
|
||||
no_temperature = true;
|
||||
}
|
||||
}
|
||||
display_temperature();
|
||||
|
||||
if (enable_trace) {
|
||||
display_pinned_message(0, 0,"CPU Trace");
|
||||
display_pinned_message(1, 0,"--- ----------------------------------------------------------------------------");
|
||||
|
@ -273,8 +293,9 @@ static void global_init(void)
|
|||
for (int i = 0; i < pm_map_size; i++) {
|
||||
trace(0, "pm %0*x - %0*x", 2*sizeof(uintptr_t), pm_map[i].start, 2*sizeof(uintptr_t), pm_map[i].end);
|
||||
}
|
||||
if (rsdp_addr != 0) {
|
||||
trace(0, "ACPI RSDP found in %s at %0*x", rsdp_source, 2*sizeof(uintptr_t), rsdp_addr);
|
||||
if (acpi_config.rsdp_addr != 0) {
|
||||
trace(0, "ACPI RSDP (v%u.%u) found in %s at %0*x", acpi_config.ver_maj, acpi_config.ver_min, rsdp_source, 2*sizeof(uintptr_t), acpi_config.rsdp_addr);
|
||||
trace(0, "ACPI FADT found at %0*x", 2*sizeof(uintptr_t), acpi_config.fadt_addr);
|
||||
}
|
||||
if (!load_addr_ok) {
|
||||
trace(0, "Cannot relocate program. Press any key to reboot...");
|
||||
|
@ -292,6 +313,30 @@ static void global_init(void)
|
|||
restart = false;
|
||||
}
|
||||
|
||||
static void ap_enumerate(int my_cpu)
|
||||
{
|
||||
if (!cpuid_info.topology.is_hybrid) {
|
||||
return;
|
||||
}
|
||||
|
||||
hybrid_core_type[my_cpu] = get_ap_hybrid_type();
|
||||
|
||||
if (hybrid_core_type[my_cpu] == CORE_PCORE) {
|
||||
cpuid_info.topology.pcore_count++;
|
||||
} else if (hybrid_core_type[my_cpu] == CORE_ECORE) {
|
||||
cpuid_info.topology.ecore_count++;
|
||||
}
|
||||
|
||||
if (hybrid_core_type[my_cpu] == CORE_ECORE && exclude_ecores) {
|
||||
cpu_state[my_cpu] = CPU_STATE_DISABLED;
|
||||
//TODO : hlt AP?
|
||||
}
|
||||
|
||||
if (my_cpu == num_enabled_cpus - 1) {
|
||||
display_cpu_topology();
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_vm_map(uintptr_t win_start, uintptr_t win_end)
|
||||
{
|
||||
vm_map_size = 0;
|
||||
|
@ -491,6 +536,7 @@ void main(void)
|
|||
} else {
|
||||
trace(my_cpu, "AP started");
|
||||
cpu_state[my_cpu] = CPU_STATE_RUNNING;
|
||||
ap_enumerate(my_cpu);
|
||||
while (init_state < 2) {
|
||||
usleep(100);
|
||||
}
|
||||
|
@ -624,7 +670,9 @@ void main(void)
|
|||
display_pass_count(pass_num);
|
||||
if (error_count == 0) {
|
||||
display_status("Pass ");
|
||||
display_notice("** Pass completed, no errors **");
|
||||
display_big_status(true);
|
||||
} else {
|
||||
display_big_status(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
app/version.h
Normal file
2
app/version.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
#define MT_VERSION "6.20"
|
||||
#define GIT_HASH "unknown"
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boot.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t orig_x;
|
||||
uint8_t orig_y;
|
||||
|
@ -56,6 +58,7 @@ typedef struct {
|
|||
|
||||
#define VIDEO_TYPE_VLFB 0x23 // VESA VGA in graphic mode
|
||||
#define VIDEO_TYPE_EFI 0x70 // EFI graphic mode
|
||||
#define VIDEO_TYPE_NONE 0xff // no video display (added for Memtest86+)
|
||||
|
||||
#define LFB_CAPABILITY_64BIT_BASE (1 << 1)
|
||||
|
||||
|
|
17
boot/efi.h
17
boot/efi.h
|
@ -38,6 +38,7 @@
|
|||
/**
|
||||
* EFI_ALLOCATE_TYPE values.
|
||||
*/
|
||||
#define EFI_ALLOCATE_MAX_ADDRESS 1
|
||||
#define EFI_ALLOCATE_ADDRESS 2
|
||||
|
||||
/**
|
||||
|
@ -285,4 +286,20 @@ typedef struct {
|
|||
efi_config_table_t *config_tables;
|
||||
} efi_system_table_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t revision;
|
||||
efi_handle_t parent_handle;
|
||||
efi_system_table_t *system_table;
|
||||
efi_handle_t device_handle;
|
||||
void *file_path;
|
||||
void *reserved;
|
||||
uint32_t load_options_size;
|
||||
void *load_options;
|
||||
void *image_base;
|
||||
uint64_t image_size;
|
||||
int image_code_type;
|
||||
int image_data_type;
|
||||
void *unload;
|
||||
} efi_loaded_image_t;
|
||||
|
||||
#endif /* EFI_H */
|
||||
|
|
132
boot/efisetup.c
132
boot/efisetup.c
|
@ -33,6 +33,7 @@
|
|||
|
||||
static efi_guid_t EFI_CONSOLE_OUT_DEVICE_GUID = { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
||||
static efi_guid_t EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a} };
|
||||
static efi_guid_t EFI_LOADED_IMAGE_PROTOCOL_GUID = { 0x5b1b31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b} };
|
||||
|
||||
static efi_system_table_t *sys_table = NULL;
|
||||
|
||||
|
@ -168,6 +169,43 @@ static void test_frame_buffer(screen_info_t *si)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int get_cmd_line_length(efi_loaded_image_t *image)
|
||||
{
|
||||
// We only use ASCII characters in our command line options, so for simplicity
|
||||
// just truncate the command line if we find a non-ASCII character.
|
||||
efi_char16_t *cmd_line = (efi_char16_t *)image->load_options;
|
||||
int max_length = image->load_options_size / sizeof(efi_char16_t);
|
||||
int length = 0;
|
||||
while (length < max_length && cmd_line[length] > 0x00 && cmd_line[length] < 0x80) {
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void get_cmd_line(efi_loaded_image_t *image, int num_chars, char *buffer)
|
||||
{
|
||||
efi_char16_t *cmd_line = (efi_char16_t *)image->load_options;
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
buffer[i] = cmd_line[i];
|
||||
}
|
||||
buffer[num_chars] = '\0';
|
||||
}
|
||||
|
||||
static efi_status_t alloc_memory(void **ptr, size_t size, efi_phys_addr_t max_addr)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
size_t num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
efi_phys_addr_t addr = max_addr;
|
||||
status = efi_call_bs(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA, num_pages, &addr);
|
||||
if (status == EFI_SUCCESS) {
|
||||
*ptr = (void *)(uintptr_t)addr;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_memory_desc_t *get_memory_desc(uintptr_t map_addr, size_t desc_size, size_t n)
|
||||
{
|
||||
return (efi_memory_desc_t *)(map_addr + n * desc_size);
|
||||
|
@ -221,60 +259,6 @@ fail:
|
|||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t alloc_low_memory(void **ptr, size_t size, efi_phys_addr_t min_addr)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
efi_memory_desc_t *mem_map = NULL;
|
||||
uintn_t mem_map_size = 0;
|
||||
uintn_t mem_desc_size = 0;
|
||||
uint32_t mem_desc_version = 0;
|
||||
uintn_t mem_map_key = 0;
|
||||
uintn_t map_buffer_size = 0;
|
||||
|
||||
status = get_memory_map(&mem_map, &mem_map_size, &mem_desc_size, &mem_desc_version, &mem_map_key, &map_buffer_size);
|
||||
if (status != EFI_SUCCESS) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size_t num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
size_t num_descs = mem_map_size / mem_desc_size;
|
||||
|
||||
for (size_t i = 0; i < num_descs; i++) {
|
||||
efi_memory_desc_t *desc = get_memory_desc((uintptr_t)mem_map, mem_desc_size, i);
|
||||
|
||||
if (desc->type != EFI_CONVENTIONAL_MEMORY) {
|
||||
continue;
|
||||
}
|
||||
if (desc->num_pages < num_pages) {
|
||||
continue;
|
||||
}
|
||||
|
||||
efi_phys_addr_t start = desc->phys_addr;
|
||||
efi_phys_addr_t end = start + desc->num_pages * PAGE_SIZE;
|
||||
|
||||
if (start < min_addr) {
|
||||
start = min_addr;
|
||||
}
|
||||
start = round_up(start, PAGE_SIZE);
|
||||
if ((start + size) > end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
status = efi_call_bs(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, num_pages, &start);
|
||||
if (status == EFI_SUCCESS) {
|
||||
*ptr = (void *)(uintptr_t)start;
|
||||
efi_call_bs(free_pool, mem_map);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
efi_call_bs(free_pool, mem_map);
|
||||
status = EFI_NOT_FOUND;
|
||||
|
||||
fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void get_bit_range(uint32_t mask, uint8_t *pos, uint8_t *size)
|
||||
{
|
||||
int first = 0;
|
||||
|
@ -350,9 +334,7 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
|
|||
|
||||
efi_graphics_output_t *gop = find_gop(handles, handles_size);
|
||||
if (!gop) {
|
||||
#if DEBUG
|
||||
print_string("GOP not found\n");
|
||||
#endif
|
||||
print_string("No graphics display found\n");
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
@ -381,9 +363,7 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
|
|||
efi_call_bs(free_pool, info);
|
||||
}
|
||||
if (best_mode == UINT32_MAX) {
|
||||
#if DEBUG
|
||||
print_string("No suitable GOP screen resolution\n");
|
||||
#endif
|
||||
print_string("No suitable screen resolution found\n");
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
@ -485,9 +465,7 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
|
|||
|
||||
status = efi_call_proto(gop, set_mode, best_mode);
|
||||
if (status != EFI_SUCCESS) {
|
||||
#if DEBUG
|
||||
print_string("Set GOP mode failed\n");
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -518,7 +496,16 @@ static efi_status_t set_screen_info(boot_params_t *boot_params)
|
|||
if (status == EFI_SUCCESS) {
|
||||
status = set_screen_info_from_gop(&boot_params->screen_info, handles, handles_size);
|
||||
}
|
||||
if (status == EFI_NOT_FOUND) {
|
||||
// This may be a headless system. We can still output to a serial console.
|
||||
boot_params->screen_info.orig_video_isVGA = VIDEO_TYPE_NONE;
|
||||
status = EFI_SUCCESS;
|
||||
}
|
||||
efi_call_bs(free_pool, handles);
|
||||
} else if (status == EFI_NOT_FOUND) {
|
||||
// This may be a headless system. We can still output to a serial console.
|
||||
boot_params->screen_info.orig_video_isVGA = VIDEO_TYPE_NONE;
|
||||
status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -575,7 +562,7 @@ static efi_status_t set_efi_info_and_exit_boot_services(efi_handle_t handle, boo
|
|||
boot_params->efi_info.mem_desc_version = mem_desc_version;
|
||||
boot_params->efi_info.mem_map = (uintptr_t)mem_map;
|
||||
boot_params->efi_info.mem_map_size = mem_map_size;
|
||||
#ifdef __X86_64__
|
||||
#ifdef __x86_64__
|
||||
boot_params->efi_info.sys_tab_hi = (uintptr_t)sys_table >> 32;
|
||||
boot_params->efi_info.mem_map_hi = (uintptr_t)mem_map >> 32;
|
||||
#endif
|
||||
|
@ -587,7 +574,7 @@ fail:
|
|||
static void set_e820_map(boot_params_t *params)
|
||||
{
|
||||
uintptr_t mem_map_addr = params->efi_info.mem_map;
|
||||
#ifdef __X86_64__
|
||||
#ifdef __x86_64__
|
||||
mem_map_addr |= (uintptr_t)params->efi_info.mem_map_hi << 32;
|
||||
#endif
|
||||
size_t mem_map_size = params->efi_info.mem_map_size;
|
||||
|
@ -647,12 +634,27 @@ boot_params_t *efi_setup(efi_handle_t handle, efi_system_table_t *sys_table_arg,
|
|||
}
|
||||
|
||||
if (boot_params == NULL) {
|
||||
status = alloc_low_memory((void **)&boot_params, sizeof(boot_params_t), 0);
|
||||
efi_loaded_image_t *image;
|
||||
status = efi_call_bs(handle_protocol, handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, (void **)&image);
|
||||
if (status != EFI_SUCCESS) {
|
||||
print_string("failed to get handle for loaded image protocol\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int cmd_line_length = get_cmd_line_length(image);
|
||||
|
||||
// Allocate below 3GB to avoid having to remap.
|
||||
status = alloc_memory((void **)&boot_params, sizeof(boot_params_t) + cmd_line_length + 1, 0xbfffffff);
|
||||
if (status != EFI_SUCCESS) {
|
||||
print_string("failed to allocate low memory for boot params\n");
|
||||
goto fail;
|
||||
}
|
||||
memset(boot_params, 0, sizeof(boot_params_t));
|
||||
|
||||
uintptr_t cmd_line_addr = (uintptr_t)boot_params + sizeof(boot_params_t);
|
||||
get_cmd_line(image, cmd_line_length, (char *)cmd_line_addr);
|
||||
boot_params->cmd_line_ptr = cmd_line_addr;
|
||||
boot_params->cmd_line_size = cmd_line_length + 1;
|
||||
}
|
||||
|
||||
boot_params->code32_start = (uintptr_t)startup32;
|
||||
|
|
116
boot/header.S
116
boot/header.S
|
@ -7,7 +7,7 @@
|
|||
// end of the boot sector), with the remainder of the header being provided by
|
||||
// setup.S.
|
||||
//
|
||||
// Copyright (C) 2020 Martin Whitaker.
|
||||
// Copyright (C) 2020-2023 Martin Whitaker.
|
||||
//
|
||||
// Derived from Linux 5.6 arch/x86/boot/header.S:
|
||||
//
|
||||
|
@ -21,13 +21,13 @@
|
|||
#define __ASSEMBLY__
|
||||
|
||||
#include "boot.h"
|
||||
#include "peimage.h"
|
||||
|
||||
# The EFI loader loads the header at ImageBase, so we have to locate the main program
|
||||
# after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load
|
||||
# address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code.
|
||||
|
||||
#define IMAGE_BASE 0x200000
|
||||
#define BASE_OF_CODE 0x1000
|
||||
|
||||
.section ".header", "ax", @progbits
|
||||
.code16
|
||||
|
@ -85,50 +85,46 @@ pe_header:
|
|||
|
||||
coff_header:
|
||||
#ifdef __x86_64__
|
||||
.word 0x8664 # Machine (x86-64)
|
||||
.word IMAGE_FILE_MACHINE_X64 # Machine (x86-64)
|
||||
#else
|
||||
.word 0x14c # Machine (i386)
|
||||
.word IMAGE_FILE_MACHINE_I386 # Machine (i386)
|
||||
#endif
|
||||
.word 1 # NumberOfSections
|
||||
.word 3 # NumberOfSections
|
||||
.long 0 # TimeDateStamp
|
||||
.long 0 # PointerToSymbolTable
|
||||
.long 0 # NumberOfSymbols
|
||||
.word section_table - optional_header # SizeOfOptionalHeader
|
||||
#ifdef __x86_64__
|
||||
.word 0x20f # Characteristics
|
||||
# IMAGE_FILE_DEBUG_STRIPPED |
|
||||
# IMAGE_FILE_LOCAL_SYMS_STRIPPED |
|
||||
# IMAGE_FILE_LINE_NUMS_STRIPPED |
|
||||
# IMAGE_FILE_EXECUTABLE_IMAGE |
|
||||
# IMAGE_FILE_RELOCS_STRIPPED
|
||||
.word IMAGE_FILE_DEBUG_STRIPPED \
|
||||
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
|
||||
| IMAGE_FILE_LINE_NUMS_STRIPPED \
|
||||
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics
|
||||
#else
|
||||
.word 0x30f # Characteristics.
|
||||
# IMAGE_FILE_32BIT_MACHINE |
|
||||
# IMAGE_FILE_DEBUG_STRIPPED |
|
||||
# IMAGE_FILE_LOCAL_SYMS_STRIPPED |
|
||||
# IMAGE_FILE_LINE_NUMS_STRIPPED |
|
||||
# IMAGE_FILE_EXECUTABLE_IMAGE |
|
||||
# IMAGE_FILE_RELOCS_STRIPPED
|
||||
.word IMAGE_FILE_32BIT_MACHINE \
|
||||
| IMAGE_FILE_DEBUG_STRIPPED \
|
||||
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
|
||||
| IMAGE_FILE_LINE_NUMS_STRIPPED \
|
||||
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics.
|
||||
#endif
|
||||
|
||||
optional_header:
|
||||
#ifdef __x86_64__
|
||||
.word 0x20b # PE32+ format
|
||||
.word IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format
|
||||
#else
|
||||
.word 0x10b # PE32 format
|
||||
.word IMAGE_NT_OPTIONAL_HDR32_MAGIC # PE32 format
|
||||
#endif
|
||||
.byte 0x02 # MajorLinkerVersion
|
||||
.byte 0x14 # MinorLinkerVersion
|
||||
|
||||
.long _text_size # SizeOfCode
|
||||
.long 0 # SizeOfInitializedData
|
||||
.long _virt_text_size # SizeOfCode
|
||||
.long _virt_sbat_size # SizeOfInitializedData
|
||||
.long 0 # SizeOfUninitializedData
|
||||
|
||||
.long BASE_OF_CODE + 0x1e0 # AddressOfEntryPoint
|
||||
.long _virt_text_start + 0x1e0 # AddressOfEntryPoint
|
||||
|
||||
.long BASE_OF_CODE # BaseOfCode
|
||||
.long _virt_text_start # BaseOfCode
|
||||
#ifndef __x86_64__
|
||||
.long 0 # data
|
||||
.long _virt_sbat_start # BaseOfData
|
||||
#endif
|
||||
|
||||
extra_header_fields:
|
||||
|
@ -147,8 +143,8 @@ extra_header_fields:
|
|||
.word 0 # MinorSubsystemVersion
|
||||
.long 0 # Win32VersionValue
|
||||
|
||||
.long BASE_OF_CODE + _init_size # SizeOfImage
|
||||
.long 512 # SizeOfHeaders
|
||||
.long _virt_img_size # SizeOfImage
|
||||
.long _file_head_size # SizeOfHeaders
|
||||
.long 0 # CheckSum
|
||||
.word 10 # Subsystem (EFI application)
|
||||
.word 0 # DllCharacteristics
|
||||
|
@ -164,7 +160,20 @@ extra_header_fields:
|
|||
.long 0 # SizeOfHeapCommit
|
||||
#endif
|
||||
.long 0 # LoaderFlags
|
||||
.long 0 # NumberOfRvaAndSizes
|
||||
.long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes
|
||||
|
||||
.long 0 # DataDirectory.Export.VirtualAddress
|
||||
.long 0 # DataDirectory.Export.Size
|
||||
.long 0 # DataDirectory.Import.VirtualAddress
|
||||
.long 0 # DataDirectory.Import.Size
|
||||
.long 0 # DataDirectory.Resource.VirtualAddress
|
||||
.long 0 # DataDirectory.Resource.Size
|
||||
.long 0 # DataDirectory.Exception.VirtualAddress
|
||||
.long 0 # DataDirectory.Exception.Size
|
||||
.long 0 # DataDirectory.Certs.VirtualAddress
|
||||
.long 0 # DataDirectory.Certs.Size
|
||||
.long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress
|
||||
.long _real_reloc_size # DataDirectory.BaseReloc.Size
|
||||
|
||||
# Section table
|
||||
section_table:
|
||||
|
@ -172,15 +181,46 @@ section_table:
|
|||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _text_size # VirtualSize
|
||||
.long BASE_OF_CODE # VirtualAddress
|
||||
.long _text_size # SizeOfRawData
|
||||
.long _text_start # PointerToRawData
|
||||
.long _virt_text_size # VirtualSize
|
||||
.long _virt_text_start # VirtualAddress
|
||||
.long _file_text_size # SizeOfRawData
|
||||
.long _file_text_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.word 0 # NumberOfRelocations
|
||||
.word 0 # NumberOfLineNumbers
|
||||
.long 0x60500020 # Characteristics (section flags)
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_MEM_EXECUTE \
|
||||
| IMAGE_SCN_CNT_CODE # Characteristics (section flags)
|
||||
|
||||
.ascii ".reloc"
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _virt_reloc_size # VirtualSize
|
||||
.long _virt_reloc_start # VirtualAddress
|
||||
.long _file_reloc_size # SizeOfRawData
|
||||
.long _file_reloc_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.word 0 # NumberOfRelocations
|
||||
.word 0 # NumberOfLineNumbers
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
||||
|
||||
.ascii ".sbat"
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _virt_sbat_size # VirtualSize
|
||||
.long _virt_sbat_start # VirtualAddress
|
||||
.long _file_sbat_size # SizeOfRawData
|
||||
.long _file_sbat_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.word 0 # NumberOfRelocations
|
||||
.word 0 # NumberOfLineNumbers
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
||||
|
||||
# Emulate the Linux boot header, to allow loading by intermediate boot loaders.
|
||||
|
||||
|
@ -199,3 +239,13 @@ root_dev:
|
|||
.word 0
|
||||
boot_flag:
|
||||
.word 0xAA55
|
||||
|
||||
.org 512
|
||||
|
||||
.section ".reloc"
|
||||
.long 0 // Page RVA
|
||||
.long 10 // Block Size (2*4+2)
|
||||
.word (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0
|
||||
|
||||
.section ".sbat", "a", @progbits
|
||||
.incbin "../boot/sbat.csv"
|
||||
|
|
292
boot/peimage.h
Normal file
292
boot/peimage.h
Normal file
|
@ -0,0 +1,292 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// This is include/coff/pe.h from binutils-2.10.0.18
|
||||
// Copyright The Free Software Foundation
|
||||
|
||||
/* PE COFF header information */
|
||||
|
||||
#ifndef _PE_H
|
||||
#define _PE_H
|
||||
|
||||
/* NT specific file attributes. */
|
||||
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
|
||||
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
|
||||
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
|
||||
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
|
||||
#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010
|
||||
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
|
||||
#define IMAGE_FILE_16BIT_MACHINE 0x0040
|
||||
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
|
||||
#define IMAGE_FILE_32BIT_MACHINE 0x0100
|
||||
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
|
||||
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
|
||||
#define IMAGE_FILE_SYSTEM 0x1000
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
|
||||
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
|
||||
|
||||
/* Additional flags to be set for section headers to allow the NT loader to
|
||||
read and write to the section data (to replace the addresses of data in
|
||||
dlls for one thing); also to execute the section in .text's case. */
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000
|
||||
|
||||
/* Section characteristics added for ppc-nt. */
|
||||
|
||||
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */
|
||||
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
|
||||
|
||||
#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */
|
||||
#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */
|
||||
#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */
|
||||
#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */
|
||||
|
||||
#define IMAGE_SCN_MEM_FARDATA 0x00008000
|
||||
|
||||
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
|
||||
#define IMAGE_SCN_MEM_16BIT 0x00020000
|
||||
#define IMAGE_SCN_MEM_LOCKED 0x00040000
|
||||
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
|
||||
|
||||
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
|
||||
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */
|
||||
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
|
||||
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
|
||||
#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000
|
||||
#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
|
||||
#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
|
||||
#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
|
||||
#define IMAGE_SCN_ALIGN_8129BYTES 0x00e00000
|
||||
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000
|
||||
|
||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */
|
||||
|
||||
#define IMAGE_REL_BASED_ABSOLUTE 0x0000
|
||||
|
||||
/* COMDAT selection codes. */
|
||||
|
||||
#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */
|
||||
#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */
|
||||
#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */
|
||||
#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */
|
||||
#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */
|
||||
|
||||
/* Machine numbers. */
|
||||
|
||||
#define IMAGE_FILE_MACHINE_UNKNOWN 0x0
|
||||
#define IMAGE_FILE_MACHINE_ALPHA 0x184
|
||||
#define IMAGE_FILE_MACHINE_ARM 0x1c0
|
||||
#define IMAGE_FILE_MACHINE_ALPHA64 0x284
|
||||
#define IMAGE_FILE_MACHINE_I386 0x14c
|
||||
#define IMAGE_FILE_MACHINE_IA64 0x200
|
||||
#define IMAGE_FILE_MACHINE_M68K 0x268
|
||||
#define IMAGE_FILE_MACHINE_MIPS16 0x266
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU 0x366
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x466
|
||||
#define IMAGE_FILE_MACHINE_POWERPC 0x1f0
|
||||
#define IMAGE_FILE_MACHINE_R3000 0x162
|
||||
#define IMAGE_FILE_MACHINE_R4000 0x166
|
||||
#define IMAGE_FILE_MACHINE_R10000 0x168
|
||||
#define IMAGE_FILE_MACHINE_SH3 0x1a2
|
||||
#define IMAGE_FILE_MACHINE_SH4 0x1a6
|
||||
#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x1c2
|
||||
#define IMAGE_FILE_MACHINE_X64 0x8664
|
||||
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
|
||||
|
||||
#define IMAGE_SUBSYSTEM_UNKNOWN 0
|
||||
#define IMAGE_SUBSYSTEM_NATIVE 1
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
|
||||
#define IMAGE_SUBSYSTEM_POSIX_CUI 7
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
|
||||
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
|
||||
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
|
||||
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
|
||||
|
||||
/* Magic values that are true for all dos/nt implementations */
|
||||
#define DOSMAGIC 0x5a4d
|
||||
#define NT_SIGNATURE 0x00004550
|
||||
|
||||
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010b
|
||||
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020b
|
||||
|
||||
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
|
||||
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
|
||||
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
|
||||
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
|
||||
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
|
||||
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
|
||||
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
|
||||
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
|
||||
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
|
||||
#define IMAGE_DIRECTORY_ENTRY_TLS 9
|
||||
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
|
||||
|
||||
#define IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
|
||||
|
||||
/* NT allows long filenames, we want to accommodate this. This may break
|
||||
some of the bfd functions */
|
||||
#undef FILNMLEN
|
||||
#define FILNMLEN 18 /* # characters in a file name */
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
struct external_PEI_filehdr
|
||||
{
|
||||
/* DOS header fields - always at offset zero in the EXE file */
|
||||
char e_magic[2]; /* Magic number, 0x5a4d */
|
||||
char e_cblp[2]; /* Bytes on last page of file, 0x90 */
|
||||
char e_cp[2]; /* Pages in file, 0x3 */
|
||||
char e_crlc[2]; /* Relocations, 0x0 */
|
||||
char e_cparhdr[2]; /* Size of header in paragraphs, 0x4 */
|
||||
char e_minalloc[2]; /* Minimum extra paragraphs needed, 0x0 */
|
||||
char e_maxalloc[2]; /* Maximum extra paragraphs needed, 0xFFFF */
|
||||
char e_ss[2]; /* Initial (relative) SS value, 0x0 */
|
||||
char e_sp[2]; /* Initial SP value, 0xb8 */
|
||||
char e_csum[2]; /* Checksum, 0x0 */
|
||||
char e_ip[2]; /* Initial IP value, 0x0 */
|
||||
char e_cs[2]; /* Initial (relative) CS value, 0x0 */
|
||||
char e_lfarlc[2]; /* File address of relocation table, 0x40 */
|
||||
char e_ovno[2]; /* Overlay number, 0x0 */
|
||||
char e_res[4][2]; /* Reserved words, all 0x0 */
|
||||
char e_oemid[2]; /* OEM identifier (for e_oeminfo), 0x0 */
|
||||
char e_oeminfo[2]; /* OEM information; e_oemid specific, 0x0 */
|
||||
char e_res2[10][2]; /* Reserved words, all 0x0 */
|
||||
char e_lfanew[4]; /* File address of new exe header, usually 0x80 */
|
||||
char dos_message[16][4]; /* other stuff, always follow DOS header */
|
||||
|
||||
/* Note: additional bytes may be inserted before the signature. Use
|
||||
the e_lfanew field to find the actual location of the NT signature */
|
||||
|
||||
char nt_signature[4]; /* required NT signature, 0x4550 */
|
||||
|
||||
/* From standard header */
|
||||
|
||||
char f_magic[2]; /* magic number */
|
||||
char f_nscns[2]; /* number of sections */
|
||||
char f_timdat[4]; /* time & date stamp */
|
||||
char f_symptr[4]; /* file pointer to symtab */
|
||||
char f_nsyms[4]; /* number of symtab entries */
|
||||
char f_opthdr[2]; /* sizeof(optional hdr) */
|
||||
char f_flags[2]; /* flags */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef COFF_IMAGE_WITH_PE
|
||||
|
||||
/* The filehdr is only weird in images. */
|
||||
|
||||
#undef FILHDR
|
||||
#define FILHDR struct external_PEI_filehdr
|
||||
#undef FILHSZ
|
||||
#define FILHSZ 152
|
||||
|
||||
#endif /* COFF_IMAGE_WITH_PE */
|
||||
|
||||
/* 32-bit PE a.out header: */
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
typedef struct
|
||||
{
|
||||
AOUTHDR standard;
|
||||
|
||||
/* NT extra fields; see internal.h for descriptions */
|
||||
char ImageBase[4];
|
||||
char SectionAlignment[4];
|
||||
char FileAlignment[4];
|
||||
char MajorOperatingSystemVersion[2];
|
||||
char MinorOperatingSystemVersion[2];
|
||||
char MajorImageVersion[2];
|
||||
char MinorImageVersion[2];
|
||||
char MajorSubsystemVersion[2];
|
||||
char MinorSubsystemVersion[2];
|
||||
char Reserved1[4];
|
||||
char SizeOfImage[4];
|
||||
char SizeOfHeaders[4];
|
||||
char CheckSum[4];
|
||||
char Subsystem[2];
|
||||
char DllCharacteristics[2];
|
||||
char SizeOfStackReserve[4];
|
||||
char SizeOfStackCommit[4];
|
||||
char SizeOfHeapReserve[4];
|
||||
char SizeOfHeapCommit[4];
|
||||
char LoaderFlags[4];
|
||||
char NumberOfRvaAndSizes[4];
|
||||
/* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
|
||||
char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
|
||||
} PEAOUTHDR;
|
||||
#endif
|
||||
#undef AOUTSZ
|
||||
#define AOUTSZ (AOUTHDRSZ + 196)
|
||||
|
||||
/* Like PEAOUTHDR, except that the "standard" member has no BaseOfData
|
||||
(aka data_start) member and that some of the members are 8 instead
|
||||
of just 4 bytes long. */
|
||||
#ifndef __ASSEMBLY__
|
||||
typedef struct
|
||||
{
|
||||
AOUTHDR standard;
|
||||
|
||||
/* NT extra fields; see internal.h for descriptions */
|
||||
char ImageBase[8];
|
||||
char SectionAlignment[4];
|
||||
char FileAlignment[4];
|
||||
char MajorOperatingSystemVersion[2];
|
||||
char MinorOperatingSystemVersion[2];
|
||||
char MajorImageVersion[2];
|
||||
char MinorImageVersion[2];
|
||||
char MajorSubsystemVersion[2];
|
||||
char MinorSubsystemVersion[2];
|
||||
char Reserved1[4];
|
||||
char SizeOfImage[4];
|
||||
char SizeOfHeaders[4];
|
||||
char CheckSum[4];
|
||||
char Subsystem[2];
|
||||
char DllCharacteristics[2];
|
||||
char SizeOfStackReserve[8];
|
||||
char SizeOfStackCommit[8];
|
||||
char SizeOfHeapReserve[8];
|
||||
char SizeOfHeapCommit[8];
|
||||
char LoaderFlags[4];
|
||||
char NumberOfRvaAndSizes[4];
|
||||
/* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
|
||||
char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
|
||||
} PEP64AOUTHDR;
|
||||
#endif
|
||||
#define PEP64AOUTSZ 240
|
||||
|
||||
#undef E_FILNMLEN
|
||||
#define E_FILNMLEN 18 /* # characters in a file name */
|
||||
|
||||
/* Import Tyoes fot ILF format object files.. */
|
||||
#define IMPORT_CODE 0
|
||||
#define IMPORT_DATA 1
|
||||
#define IMPORT_CONST 2
|
||||
|
||||
/* Import Name Tyoes for ILF format object files. */
|
||||
#define IMPORT_ORDINAL 0
|
||||
#define IMPORT_NAME 1
|
||||
#define IMPORT_NAME_NOPREFIX 2
|
||||
#define IMPORT_NAME_UNDECORATE 3
|
||||
|
||||
#endif /* _PE_H */
|
2
boot/sbat.csv
Normal file
2
boot/sbat.csv
Normal file
|
@ -0,0 +1,2 @@
|
|||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
memtest86+,1,Memtest86+,6.0,https://github.com/memtest86plus
|
|
11
boot/setup.S
11
boot/setup.S
|
@ -18,6 +18,7 @@
|
|||
#define __ASSEMBLY__
|
||||
|
||||
#include "boot.h"
|
||||
#include "build_version.h"
|
||||
|
||||
#define BOOT_PARAMS_START (SETUP_SECS * 512)
|
||||
#define BOOT_PARAMS_END (BOOT_PARAMS_START + 4096)
|
||||
|
@ -41,7 +42,7 @@ realmode_swtch:
|
|||
start_sys_seg:
|
||||
.word 0x1000
|
||||
kernel_version:
|
||||
.word 0
|
||||
.word mt86plus_version-512
|
||||
type_of_loader:
|
||||
.byte 0
|
||||
loadflags:
|
||||
|
@ -68,11 +69,11 @@ cmd_line_ptr:
|
|||
initrd_addr_max:
|
||||
.long 0xffffffff
|
||||
kernel_alignment:
|
||||
.long 0
|
||||
.long 4096
|
||||
relocatable_kernel:
|
||||
.byte 0
|
||||
min_alignment:
|
||||
.byte 0
|
||||
.byte 12
|
||||
xload_flags:
|
||||
#ifdef __x86_64__
|
||||
.word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64
|
||||
|
@ -385,6 +386,10 @@ idt_descr:
|
|||
.word 0 # idt limit=0
|
||||
.long 0 # idt base=0
|
||||
|
||||
mt86plus_version:
|
||||
.ascii "Memtest86+ v" , MT_VERSION
|
||||
.byte 0
|
||||
|
||||
# Pad to the declared size.
|
||||
|
||||
.org (SETUP_SECS*512)
|
||||
|
|
|
@ -744,7 +744,7 @@ ap_trampoline:
|
|||
movl %edi, %eax
|
||||
addl $(gdt - startup), %eax
|
||||
movl %eax, (ap_gdt_descr - ap_trampoline + 2)
|
||||
lgdt ap_gdt_descr - ap_trampoline
|
||||
data32 lgdt ap_gdt_descr - ap_trampoline
|
||||
|
||||
# Switch to protected mode and reload the segment registers.
|
||||
|
||||
|
@ -812,7 +812,7 @@ startup_stack_top:
|
|||
|
||||
# Main stack area.
|
||||
|
||||
.section "stacks", "aw", @progbits
|
||||
.section ".stacks", "aw", @nobits
|
||||
.align 16
|
||||
|
||||
. = . + STACKS_SIZE
|
||||
|
|
|
@ -556,7 +556,7 @@ ap_trampoline:
|
|||
movl %ebx, %eax
|
||||
addl $(gdt - startup), %eax
|
||||
movl %eax, (ap_gdt_descr - ap_trampoline + 2)
|
||||
lgdt ap_gdt_descr - ap_trampoline
|
||||
data32 lgdt ap_gdt_descr - ap_trampoline
|
||||
|
||||
# Set the page directory base address.
|
||||
|
||||
|
|
|
@ -14,12 +14,15 @@ CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m32 -march=i586 -fpic -fno-builtin \
|
|||
|
||||
INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp
|
||||
|
||||
SYS_OBJS = system/cpuid.o \
|
||||
SYS_OBJS = system/acpi.o \
|
||||
system/cpuid.o \
|
||||
system/cpuinfo.o \
|
||||
system/cpulocal.o \
|
||||
system/ehci.o \
|
||||
system/font.o \
|
||||
system/heap.o \
|
||||
system/hwctrl.o \
|
||||
system/hwquirks.o \
|
||||
system/keyboard.o \
|
||||
system/ohci.o \
|
||||
system/pci.o \
|
||||
|
@ -31,13 +34,13 @@ SYS_OBJS = system/cpuid.o \
|
|||
system/smbus.o \
|
||||
system/smp.o \
|
||||
system/temperature.o \
|
||||
system/timers.o \
|
||||
system/uhci.o \
|
||||
system/usbhcd.o \
|
||||
system/vmem.o \
|
||||
system/xhci.o
|
||||
|
||||
LIB_OBJS = lib/barrier.o \
|
||||
lib/ctype.o \
|
||||
lib/div64.o \
|
||||
lib/print.o \
|
||||
lib/read.o \
|
||||
|
@ -72,16 +75,15 @@ all: memtest.bin memtest.efi
|
|||
-include $(subst .o,.d,$(TST_OBJS))
|
||||
-include $(subst .o,.d,$(APP_OBJS))
|
||||
|
||||
boot/%.o: boot/%.s
|
||||
$(AS) $< -o $@
|
||||
boot/header.o : | ../boot/sbat.csv
|
||||
|
||||
boot/startup.s: ../boot/startup32.S ../boot/boot.h
|
||||
boot/startup.o: ../boot/startup32.S ../boot/boot.h
|
||||
@mkdir -p boot
|
||||
$(CC) -m32 -E -traditional -I../boot -o $@ $<
|
||||
$(CC) -m32 -x assembler-with-cpp -c -I../boot -o $@ $<
|
||||
|
||||
boot/%.s: ../boot/%.S ../boot/boot.h
|
||||
boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h
|
||||
@mkdir -p boot
|
||||
$(CC) -m32 -E -traditional -I../boot -o $@ $<
|
||||
$(CC) -m32 -x assembler-with-cpp -c -I../boot -Iapp -o $@ $<
|
||||
|
||||
boot/efisetup.o: ../boot/efisetup.c
|
||||
@mkdir -p boot
|
||||
|
@ -103,20 +105,22 @@ tests/%.o: ../tests/%.c
|
|||
@mkdir -p tests
|
||||
$(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/%.o: ../app/%.c app/githash.h
|
||||
app/%.o: ../app/%.c app/build_version.h
|
||||
@mkdir -p app
|
||||
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/githash.h: FORCE
|
||||
app/build_version.h: FORCE
|
||||
@mkdir -p app
|
||||
@( \
|
||||
cp -f ../app/version.h $@.tmp; \
|
||||
if $(GIT_AVAILABLE) && test -d ../.git ; then \
|
||||
hash=`git rev-parse HEAD | cut -c1-7`; \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \
|
||||
else \
|
||||
hash="unknown"; \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \
|
||||
fi; \
|
||||
define=`echo "#define GIT_HASH \"$$hash\""`; \
|
||||
echo $$define | diff - $@ > /dev/null 2>&1 || echo $$define > $@; \
|
||||
cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \
|
||||
rm -f $@.tmp; \
|
||||
)
|
||||
|
||||
FORCE:
|
||||
|
@ -166,8 +170,13 @@ iso: memtest.iso
|
|||
clean:
|
||||
rm -rf boot system lib tests app *.img *.iso memtest* iso grub-*
|
||||
|
||||
# grub-memtest.iso can be used for testing the various different boot modes,
|
||||
# using GRUB as an intermediate bootloader. Upstream GRUB only supports the
|
||||
# grub-memtest.iso uses GRUB as an intermediate bootloader to allow Memtest86+
|
||||
# to be started with the native USB keyboard drivers either enabled or disabled,
|
||||
# or to be started in a fail-safe mode with SMP and memory identification &
|
||||
# speed testing disabled.
|
||||
#
|
||||
# By setting GRUB_CFG to "grub-test", grub-memtest.iso can instead be used for
|
||||
# testing the various different boot modes. Upstream GRUB only supports the
|
||||
# 16-bit and 32-bit boot protocols, via the "linux16" and "linux" commands.
|
||||
# Fedora add support for the EFI handover boot protocols, via the "linuxefi"
|
||||
# command, and, since 2019, remove support for the 32-bit protocol, aliasing
|
||||
|
@ -177,6 +186,8 @@ clean:
|
|||
# doubt do their own thing. The boot menu on grub-memtest.iso attempts to
|
||||
# support all these options.
|
||||
|
||||
GRUB_CFG ?= grub
|
||||
|
||||
GRUB_FONT_DIR ?= /usr/share/grub
|
||||
|
||||
GRUB_LIB_DIR ?= /usr/lib/grub
|
||||
|
@ -192,22 +203,22 @@ grub-eltorito.img:
|
|||
grub-bootia32.efi:
|
||||
$(GRUB_MKIMAGE) --output $@ --prefix /EFI/BOOT/grub --format i386-efi $(GRUB_MODULES)
|
||||
|
||||
grub-esp.img: memtest.efi grub-bootia32.efi ../grub/grub-efi.cfg
|
||||
grub-esp.img: memtest.efi grub-bootia32.efi ../grub/${GRUB_CFG}-efi.cfg
|
||||
@mkdir -p grub-iso/EFI/BOOT/grub/i386-efi grub-iso/EFI/BOOT/grub/fonts
|
||||
cp memtest.efi grub-iso/EFI/BOOT/memtest
|
||||
cp grub-bootia32.efi grub-iso/EFI/BOOT/bootia32.efi
|
||||
cp ../grub/grub-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg
|
||||
cp ../grub/${GRUB_CFG}-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg
|
||||
cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/EFI/BOOT/grub/fonts/
|
||||
cp $(GRUB_LIB_DIR)/i386-efi/*.mod grub-iso/EFI/BOOT/grub/i386-efi/
|
||||
@rm -f grub-esp.img
|
||||
/sbin/mkdosfs -n MT86P_ESP -F12 -C grub-esp.img 8192
|
||||
mcopy -s -i grub-esp.img grub-iso/EFI ::
|
||||
|
||||
grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/grub-legacy.cfg grub-esp.img
|
||||
grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/${GRUB_CFG}-legacy.cfg grub-esp.img
|
||||
@mkdir -p grub-iso/boot/grub/i386-pc grub-iso/boot/grub/fonts
|
||||
cp memtest.bin grub-iso/boot/memtest
|
||||
cp grub-eltorito.img grub-iso/boot/eltorito.img
|
||||
cp ../grub/grub-legacy.cfg grub-iso/boot/grub/grub.cfg
|
||||
cp ../grub/${GRUB_CFG}-legacy.cfg grub-iso/boot/grub/grub.cfg
|
||||
cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/boot/grub/fonts/
|
||||
cp $(GRUB_LIB_DIR)/i386-pc/*.mod grub-iso/boot/grub/i386-pc/
|
||||
xorrisofs -pad -R -J -volid MT86PLUS_32 -graft-points -hide-rr-moved \
|
||||
|
|
|
@ -12,15 +12,48 @@ SECTIONS {
|
|||
}
|
||||
. = ALIGN(512);
|
||||
.text : {
|
||||
_text_start = . ;
|
||||
_file_text_start = . ;
|
||||
*(.data)
|
||||
_real_text_end = . ;
|
||||
. = ALIGN(512);
|
||||
_text_end = . ;
|
||||
_file_text_end = . ;
|
||||
}
|
||||
.reloc : {
|
||||
_file_reloc_start = . ;
|
||||
*(.reloc)
|
||||
_real_reloc_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_reloc_end = . ;
|
||||
}
|
||||
.sbat : {
|
||||
_file_sbat_start = . ;
|
||||
*(.sbat)
|
||||
_real_sbat_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_sbat_end = . ;
|
||||
}
|
||||
/DISCARD/ : { *(*) }
|
||||
|
||||
_text_size = (_text_end - _text_start);
|
||||
_real_text_size = _real_text_end - _file_text_start;
|
||||
_real_reloc_size = _real_reloc_end - _file_reloc_start;
|
||||
_real_sbat_size = _real_sbat_end - _file_sbat_start;
|
||||
|
||||
_sys_size = _text_size >> 4;
|
||||
_init_size = _text_size + _bss_size;
|
||||
_file_head_size = _file_text_start;
|
||||
_file_text_size = _file_text_end - _file_text_start;
|
||||
_file_reloc_size = _file_reloc_end - _file_reloc_start;
|
||||
_file_sbat_size = _file_sbat_end - _file_sbat_start;
|
||||
|
||||
_sys_size = (_real_text_size + 15) >> 4;
|
||||
_init_size = _real_text_size + _bss_size;
|
||||
|
||||
_virt_head_size = ((_file_head_size + 4095) >> 12) << 12;
|
||||
_virt_text_size = ((_init_size + 4095) >> 12) << 12;
|
||||
_virt_reloc_size = ((_file_reloc_size + 4095) >> 12) << 12;
|
||||
_virt_sbat_size = ((_file_sbat_size + 4095) >> 12) << 12;
|
||||
|
||||
_virt_text_start = _virt_head_size;
|
||||
_virt_reloc_start = _virt_text_start + _virt_text_size;
|
||||
_virt_sbat_start = _virt_reloc_start + _virt_reloc_size;
|
||||
|
||||
_virt_img_size = _virt_sbat_start + _virt_sbat_size;
|
||||
}
|
||||
|
|
|
@ -14,12 +14,15 @@ CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m64 -march=x86-64 -mno-mmx -mno-sse -m
|
|||
|
||||
INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp
|
||||
|
||||
SYS_OBJS = system/cpuid.o \
|
||||
SYS_OBJS = system/acpi.o \
|
||||
system/cpuid.o \
|
||||
system/cpuinfo.o \
|
||||
system/cpulocal.o \
|
||||
system/ehci.o \
|
||||
system/font.o \
|
||||
system/hwctrl.o \
|
||||
system/heap.o \
|
||||
system/hwquirks.o \
|
||||
system/keyboard.o \
|
||||
system/ohci.o \
|
||||
system/pci.o \
|
||||
|
@ -31,13 +34,13 @@ SYS_OBJS = system/cpuid.o \
|
|||
system/smbus.o \
|
||||
system/smp.o \
|
||||
system/temperature.o \
|
||||
system/timers.o \
|
||||
system/uhci.o \
|
||||
system/usbhcd.o \
|
||||
system/vmem.o \
|
||||
system/xhci.o
|
||||
|
||||
LIB_OBJS = lib/barrier.o \
|
||||
lib/ctype.o \
|
||||
lib/print.o \
|
||||
lib/read.o \
|
||||
lib/string.o \
|
||||
|
@ -71,16 +74,15 @@ all: memtest.bin memtest.efi
|
|||
-include $(subst .o,.d,$(TST_OBJS))
|
||||
-include $(subst .o,.d,$(APP_OBJS))
|
||||
|
||||
boot/%.o: boot/%.s
|
||||
$(AS) $< -o $@
|
||||
boot/header.o : | ../boot/sbat.csv
|
||||
|
||||
boot/startup.s: ../boot/startup64.S ../boot/boot.h
|
||||
boot/startup.o: ../boot/startup64.S ../boot/boot.h
|
||||
@mkdir -p boot
|
||||
$(CC) -E -traditional -I../boot -o $@ $<
|
||||
$(CC) -m64 -x assembler-with-cpp -c -I../boot -o $@ $<
|
||||
|
||||
boot/%.s: ../boot/%.S ../boot/boot.h
|
||||
boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h
|
||||
@mkdir -p boot
|
||||
$(CC) -E -traditional -I../boot -o $@ $<
|
||||
$(CC) -m64 -x assembler-with-cpp -c -I../boot -Iapp -o $@ $<
|
||||
|
||||
boot/efisetup.o: ../boot/efisetup.c
|
||||
@mkdir -p boot
|
||||
|
@ -102,20 +104,22 @@ tests/%.o: ../tests/%.c
|
|||
@mkdir -p tests
|
||||
$(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/%.o: ../app/%.c app/githash.h
|
||||
app/%.o: ../app/%.c app/build_version.h
|
||||
@mkdir -p app
|
||||
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/githash.h: FORCE
|
||||
app/build_version.h: FORCE
|
||||
@mkdir -p app
|
||||
@( \
|
||||
cp -f ../app/version.h $@.tmp; \
|
||||
if $(GIT_AVAILABLE) && test -d ../.git ; then \
|
||||
hash=`git rev-parse HEAD | cut -c1-7`; \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \
|
||||
else \
|
||||
hash="unknown"; \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \
|
||||
fi; \
|
||||
define=`echo "#define GIT_HASH \"$$hash\""`; \
|
||||
echo $$define | diff - $@ > /dev/null 2>&1 || echo $$define > $@; \
|
||||
cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \
|
||||
rm -f $@.tmp; \
|
||||
)
|
||||
|
||||
FORCE:
|
||||
|
@ -165,8 +169,13 @@ iso: memtest.iso
|
|||
clean:
|
||||
rm -rf boot system lib tests app *.img *.iso memtest* iso grub-*
|
||||
|
||||
# grub-memtest.iso can be used for testing the various different boot modes,
|
||||
# using GRUB as an intermediate bootloader. Upstream GRUB only supports the
|
||||
# grub-memtest.iso uses GRUB as an intermediate bootloader to allow Memtest86+
|
||||
# to be started with the native USB keyboard drivers either enabled or disabled,
|
||||
# or to be started in a fail-safe mode with SMP and memory identification &
|
||||
# speed testing disabled.
|
||||
#
|
||||
# By setting GRUB_CFG to "grub-test", grub-memtest.iso can instead be used for
|
||||
# testing the various different boot modes. Upstream GRUB only supports the
|
||||
# 16-bit and 32-bit boot protocols, via the "linux16" and "linux" commands.
|
||||
# Fedora add support for the EFI handover boot protocols, via the "linuxefi"
|
||||
# command, and, since 2019, remove support for the 32-bit protocol, aliasing
|
||||
|
@ -176,6 +185,8 @@ clean:
|
|||
# doubt do their own thing. The boot menu on grub-memtest.iso attempts to
|
||||
# support all these options.
|
||||
|
||||
GRUB_CFG ?= grub
|
||||
|
||||
GRUB_FONT_DIR ?= /usr/share/grub
|
||||
|
||||
GRUB_LIB_DIR ?= /usr/lib/grub
|
||||
|
@ -191,22 +202,22 @@ grub-eltorito.img:
|
|||
grub-bootx64.efi:
|
||||
$(GRUB_MKIMAGE) --output $@ --prefix /EFI/BOOT/grub --format x86_64-efi $(GRUB_MODULES)
|
||||
|
||||
grub-esp.img: memtest.efi grub-bootx64.efi ../grub/grub-efi.cfg
|
||||
grub-esp.img: memtest.efi grub-bootx64.efi ../grub/${GRUB_CFG}-efi.cfg
|
||||
@mkdir -p grub-iso/EFI/BOOT/grub/x86_64-efi grub-iso/EFI/BOOT/grub/fonts
|
||||
cp memtest.efi grub-iso/EFI/BOOT/memtest
|
||||
cp grub-bootx64.efi grub-iso/EFI/BOOT/bootx64.efi
|
||||
cp ../grub/grub-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg
|
||||
cp ../grub/${GRUB_CFG}-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg
|
||||
cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/EFI/BOOT/grub/fonts/
|
||||
cp $(GRUB_LIB_DIR)/x86_64-efi/*.mod grub-iso/EFI/BOOT/grub/x86_64-efi/
|
||||
@rm -f grub-esp.img
|
||||
/sbin/mkdosfs -n MT86P_ESP -F12 -C grub-esp.img 8192
|
||||
mcopy -s -i grub-esp.img grub-iso/EFI ::
|
||||
|
||||
grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/grub-legacy.cfg grub-esp.img
|
||||
grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/${GRUB_CFG}-legacy.cfg grub-esp.img
|
||||
@mkdir -p grub-iso/boot/grub/i386-pc grub-iso/boot/grub/fonts
|
||||
cp memtest.bin grub-iso/boot/memtest
|
||||
cp grub-eltorito.img grub-iso/boot/eltorito.img
|
||||
cp ../grub/grub-legacy.cfg grub-iso/boot/grub/grub.cfg
|
||||
cp ../grub/${GRUB_CFG}-legacy.cfg grub-iso/boot/grub/grub.cfg
|
||||
cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/boot/grub/fonts/
|
||||
cp $(GRUB_LIB_DIR)/i386-pc/*.mod grub-iso/boot/grub/i386-pc/
|
||||
xorrisofs -pad -R -J -volid MT86PLUS_64 -graft-points -hide-rr-moved \
|
||||
|
|
|
@ -12,15 +12,48 @@ SECTIONS {
|
|||
}
|
||||
. = ALIGN(512);
|
||||
.text : {
|
||||
_text_start = . ;
|
||||
_file_text_start = . ;
|
||||
*(.data)
|
||||
_real_text_end = . ;
|
||||
. = ALIGN(512);
|
||||
_text_end = . ;
|
||||
_file_text_end = . ;
|
||||
}
|
||||
.reloc : {
|
||||
_file_reloc_start = . ;
|
||||
*(.reloc)
|
||||
_real_reloc_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_reloc_end = . ;
|
||||
}
|
||||
.sbat : {
|
||||
_file_sbat_start = . ;
|
||||
*(.sbat)
|
||||
_real_sbat_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_sbat_end = . ;
|
||||
}
|
||||
/DISCARD/ : { *(*) }
|
||||
|
||||
_text_size = (_text_end - _text_start);
|
||||
_real_text_size = _real_text_end - _file_text_start;
|
||||
_real_reloc_size = _real_reloc_end - _file_reloc_start;
|
||||
_real_sbat_size = _real_sbat_end - _file_sbat_start;
|
||||
|
||||
_sys_size = _text_size >> 4;
|
||||
_init_size = _text_size + _bss_size;
|
||||
_file_head_size = _file_text_start;
|
||||
_file_text_size = _file_text_end - _file_text_start;
|
||||
_file_reloc_size = _file_reloc_end - _file_reloc_start;
|
||||
_file_sbat_size = _file_sbat_end - _file_sbat_start;
|
||||
|
||||
_sys_size = (_real_text_size + 15) >> 4;
|
||||
_init_size = _real_text_size + _bss_size;
|
||||
|
||||
_virt_head_size = ((_file_head_size + 4095) >> 12) << 12;
|
||||
_virt_text_size = ((_init_size + 4095) >> 12) << 12;
|
||||
_virt_reloc_size = ((_file_reloc_size + 4095) >> 12) << 12;
|
||||
_virt_sbat_size = ((_file_sbat_size + 4095) >> 12) << 12;
|
||||
|
||||
_virt_text_start = _virt_head_size;
|
||||
_virt_reloc_start = _virt_text_start + _virt_text_size;
|
||||
_virt_sbat_start = _virt_reloc_start + _virt_reloc_size;
|
||||
|
||||
_virt_img_size = _virt_sbat_start + _virt_sbat_size;
|
||||
}
|
||||
|
|
|
@ -8,15 +8,13 @@ set default=0
|
|||
set timeout=-1
|
||||
|
||||
insmod linux
|
||||
insmod linuxefi
|
||||
insmod linux32
|
||||
|
||||
menuentry "Start Memtest86+ using 'linux' command" {
|
||||
linux /EFI/BOOT/memtest
|
||||
menuentry "Start Memtest86+, use built-in support for USB keyboards" {
|
||||
linux /EFI/BOOT/memtest keyboard=both
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linuxefi' command" {
|
||||
linuxefi /EFI/BOOT/memtest
|
||||
menuentry "Start Memtest86+, use BIOS legacy emulation for USB keyboards" {
|
||||
linux /EFI/BOOT/memtest keyboard=legacy
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux32' command" {
|
||||
linux32 /EFI/BOOT/memtest
|
||||
menuentry "Start Memtest86+, disable SMP and memory identification" {
|
||||
linux /EFI/BOOT/memtest nosmp nosm nobench
|
||||
}
|
||||
|
|
|
@ -8,15 +8,13 @@ set default=0
|
|||
set timeout=-1
|
||||
|
||||
insmod linux
|
||||
insmod linux16
|
||||
insmod linux32
|
||||
|
||||
menuentry "Start Memtest86+ using 'linux' command" {
|
||||
linux /boot/memtest
|
||||
menuentry "Start Memtest86+, use built-in support for USB keyboards" {
|
||||
linux /boot/memtest keyboard=both
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux16' command" {
|
||||
linux16 /boot/memtest
|
||||
menuentry "Start Memtest86+, use BIOS legacy emulation for USB keyboards" {
|
||||
linux /boot/memtest keyboard=legacy
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux32' command" {
|
||||
linux32 /boot/memtest
|
||||
menuentry "Start Memtest86+, disable SMP and memory identification" {
|
||||
linux /boot/memtest nosmp nosm nobench
|
||||
}
|
||||
|
|
22
grub/grub-test-efi.cfg
Normal file
22
grub/grub-test-efi.cfg
Normal file
|
@ -0,0 +1,22 @@
|
|||
if loadfont unicode ; then
|
||||
set gfxmode=1024x768,800x600,auto
|
||||
set gfxpayload=800x600,1024x768
|
||||
terminal_output gfxterm
|
||||
fi
|
||||
|
||||
set default=0
|
||||
set timeout=-1
|
||||
|
||||
insmod linux
|
||||
insmod linuxefi
|
||||
insmod linux32
|
||||
|
||||
menuentry "Start Memtest86+ using 'linux' command" {
|
||||
linux /EFI/BOOT/memtest
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linuxefi' command" {
|
||||
linuxefi /EFI/BOOT/memtest
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux32' command" {
|
||||
linux32 /EFI/BOOT/memtest
|
||||
}
|
22
grub/grub-test-legacy.cfg
Normal file
22
grub/grub-test-legacy.cfg
Normal file
|
@ -0,0 +1,22 @@
|
|||
if loadfont unicode ; then
|
||||
set gfxmode=1024x768,800x600,auto
|
||||
set gfxpayload=800x600,1024x768
|
||||
terminal_output gfxterm
|
||||
fi
|
||||
|
||||
set default=0
|
||||
set timeout=-1
|
||||
|
||||
insmod linux
|
||||
insmod linux16
|
||||
insmod linux32
|
||||
|
||||
menuentry "Start Memtest86+ using 'linux' command" {
|
||||
linux /boot/memtest
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux16' command" {
|
||||
linux16 /boot/memtest
|
||||
}
|
||||
menuentry "Start Memtest86+ using 'linux32' command" {
|
||||
linux32 /boot/memtest
|
||||
}
|
27
lib/ctype.c
27
lib/ctype.c
|
@ -1,27 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020 Martin Whitaker.
|
||||
|
||||
#include "ctype.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int toupper(int c)
|
||||
{
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return c + 'A' -'a';
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
int isdigit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
int isxdigit(int c)
|
||||
{
|
||||
return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F');
|
||||
}
|
19
lib/ctype.h
19
lib/ctype.h
|
@ -14,18 +14,31 @@
|
|||
* If c is a lower-case letter, returns its upper-case equivalent, otherwise
|
||||
* returns c. Assumes c is an ASCII character.
|
||||
*/
|
||||
int toupper(int c);
|
||||
static inline int toupper(int c)
|
||||
{
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return c + 'A' -'a';
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if c is a decimal digit, otherwise returns 0. Assumes c is an
|
||||
* ASCII character.
|
||||
*/
|
||||
int isdigit(int c);
|
||||
static inline int isdigit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if c is a hexadecimal digit, otherwise returns 0. Assumes c is an
|
||||
* ASCII character.
|
||||
*/
|
||||
int isxdigit(int c);
|
||||
static inline int isxdigit(int c)
|
||||
{
|
||||
return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F');
|
||||
}
|
||||
|
||||
#endif // CTYPE_H
|
||||
|
|
54
lib/string.c
54
lib/string.c
|
@ -31,28 +31,6 @@ void reverse(char s[])
|
|||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const unsigned char *src1 = s1, *src2 = s2;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (src1[i] != src2[i]) {
|
||||
return (int)src1[i] - (int)src2[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
char *d = (char *)dest, *s = (char *)src;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n)
|
||||
{
|
||||
char *d = (char *)dest, *s = (char *)src;
|
||||
|
@ -74,38 +52,6 @@ void *memmove(void *dest, const void *src, size_t n)
|
|||
return dest;
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
char *d = (char *)s;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
d[i] = c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
size_t len = 0;
|
||||
while (*s++) {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int strncmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (s1[i] != s2[i]) {
|
||||
return (int)s1[i] - (int)s2[i];
|
||||
}
|
||||
if (s1[i] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *strstr(const char *haystack, const char *needle)
|
||||
{
|
||||
size_t haystack_len = strlen(haystack);
|
||||
|
|
40
lib/string.h
40
lib/string.h
|
@ -18,14 +18,25 @@
|
|||
* between the first mismatching byte in s1 (interpreted as an unsigned
|
||||
* value) and the corresponding byte in s2.
|
||||
*/
|
||||
int memcmp(const void *s1, const void *s2, size_t n);
|
||||
static inline int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const unsigned char *src1 = s1, *src2 = s2;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (src1[i] != src2[i]) {
|
||||
return (int)src1[i] - (int)src2[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies n bytes from the memory area pointed to by src to the memory area
|
||||
* pointed to by dest and returns a pointer to dest. The memory areas must
|
||||
* not overlap.
|
||||
* void *memcpy(void *dst, const void *src, size_t n);
|
||||
*/
|
||||
void *memcpy(void *dst, const void *src, size_t n);
|
||||
#define memcpy(d, s, n) __builtin_memcpy((d), (s), (n))
|
||||
|
||||
/**
|
||||
* Copies n bytes from the memory area pointed to by src to the memory area
|
||||
|
@ -37,13 +48,21 @@ void *memmove(void *dest, const void *src, size_t n);
|
|||
/**
|
||||
* Fills the first n bytes of the memory area pointed to by s with the byte
|
||||
* value c.
|
||||
* void *memset(void *s, int c, size_t n);
|
||||
*/
|
||||
void *memset(void *s, int c, size_t n);
|
||||
#define memset(s, c, n) __builtin_memset((s), (c), (n))
|
||||
|
||||
/**
|
||||
* Returns the string length, excluding the terminating null character.
|
||||
*/
|
||||
size_t strlen(const char *s);
|
||||
static inline size_t strlen(const char *s)
|
||||
{
|
||||
size_t len = 0;
|
||||
while (*s++) {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares at most the first n characters in the strings s1 and s2 and
|
||||
|
@ -51,7 +70,18 @@ size_t strlen(const char *s);
|
|||
* between the first mismatching character in s1 (interpreted as a signed
|
||||
* value) and the corresponding character in s2.
|
||||
*/
|
||||
int strncmp(const char *s1, const char *s2, size_t n);
|
||||
static inline int strncmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (s1[i] != s2[i]) {
|
||||
return (int)s1[i] - (int)s2[i];
|
||||
}
|
||||
if (s1[i] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first occurrence of the substring needle in the string haystack
|
||||
|
|
344
system/acpi.c
Normal file
344
system/acpi.c
Normal file
|
@ -0,0 +1,344 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
#include "efi.h"
|
||||
|
||||
#include "pmem.h"
|
||||
#include "string.h"
|
||||
#include "unistd.h"
|
||||
#include "vmem.h"
|
||||
|
||||
#include "acpi.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Table signatures
|
||||
|
||||
#define RSDPSignature1 ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24))
|
||||
#define RSDPSignature2 ('P' | ('T' << 8) | ('R' << 16) | (' ' << 24))
|
||||
|
||||
#define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24)) // Root System Description Table
|
||||
|
||||
#define XSDTSignature ('X' | ('S' << 8) | ('D' << 16) | ('T' << 24)) // Extended System Description Table
|
||||
|
||||
#define MADTSignature ('A' | ('P' << 8) | ('I' << 16) | ('C' << 24)) // Multiple APIC Description Table
|
||||
|
||||
#define FADTSignature ('F' | ('A' << 8) | ('C' << 16) | ('P' << 24)) // Fixed ACPI Description Table
|
||||
|
||||
#define HPETSignature ('H' | ('P' << 8) | ('E' << 16) | ('T' << 24)) // High Precision Event Timer
|
||||
|
||||
#define EINJSignature ('E' | ('I' << 8) | ('N' << 16) | ('J' << 24)) // Error Injection Table
|
||||
#define ERSTSignature ('E' | ('R' << 8) | ('S' << 16) | ('T' << 24)) // Error Record Serialization Table
|
||||
#define CPEPSignature ('C' | ('P' << 8) | ('E' << 16) | ('P' << 24)) // Corrected Platform Error Polling Table
|
||||
#define HESTSignature ('H' | ('E' << 8) | ('S' << 16) | ('T' << 24)) // Hardware Error Source Table
|
||||
|
||||
#define SLITSignature ('S' | ('L' << 8) | ('I' << 16) | ('T' << 24)) // System Locality Information Table (NUMA)
|
||||
#define SRATSignature ('S' | ('R' << 8) | ('A' << 16) | ('T' << 24)) // System Resource Affinity Table (NUMA)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint8_t address_space;
|
||||
uint8_t bit_width;
|
||||
uint8_t bit_offset;
|
||||
uint8_t access_size;
|
||||
uint64_t address;
|
||||
} acpi_gen_addr_struct;
|
||||
|
||||
typedef struct {
|
||||
char signature[8]; // "RSD PTR "
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_addr;
|
||||
uint32_t length;
|
||||
uint64_t xsdt_addr;
|
||||
uint8_t xchecksum;
|
||||
uint8_t reserved[3];
|
||||
} rsdp_t;
|
||||
|
||||
typedef struct {
|
||||
char signature[4]; // "RSDT" or "XSDT"
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table_id[8];
|
||||
char oem_revision[4];
|
||||
char creator_id[4];
|
||||
char creator_revision[4];
|
||||
} rsdt_header_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const efi_guid_t EFI_ACPI_1_RDSP_GUID = { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
||||
static const efi_guid_t EFI_ACPI_2_RDSP_GUID = { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81} };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const char *rsdp_source = "";
|
||||
|
||||
acpi_t acpi_config = {0, 0, 0, 0, 0, 0, 0, false};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)addr;
|
||||
uint32_t *end = ptr + length / sizeof(uint32_t);
|
||||
|
||||
while (ptr < end) {
|
||||
rsdp_t *rp = (rsdp_t *)ptr;
|
||||
if (*ptr == RSDPSignature1 && *(ptr+1) == RSDPSignature2 && acpi_checksum(ptr, 20) == 0) {
|
||||
if (rp->revision < 2 || (rp->length < 1024 && acpi_checksum(ptr, rp->length) == 0)) {
|
||||
return rp;
|
||||
}
|
||||
}
|
||||
ptr += 4;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static rsdp_t *find_rsdp_in_efi64_system_table(efi64_system_table_t *system_table)
|
||||
{
|
||||
efi64_config_table_t *config_tables = (efi64_config_table_t *)map_region(system_table->config_tables,
|
||||
system_table->num_config_tables * sizeof(efi64_config_table_t),
|
||||
true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
uintptr_t table_addr = 0;
|
||||
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
break;
|
||||
}
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
}
|
||||
}
|
||||
return (rsdp_t *)table_addr;
|
||||
}
|
||||
#else
|
||||
static rsdp_t *find_rsdp_in_efi32_system_table(efi32_system_table_t *system_table)
|
||||
{
|
||||
efi32_config_table_t *config_tables = (efi32_config_table_t *)map_region(system_table->config_tables,
|
||||
system_table->num_config_tables * sizeof(efi32_config_table_t),
|
||||
true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
uintptr_t table_addr = 0;
|
||||
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
break;
|
||||
}
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
}
|
||||
}
|
||||
return (rsdp_t *)table_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static uintptr_t find_rsdp(void)
|
||||
{
|
||||
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
||||
|
||||
const efi_info_t *efi_info = &boot_params->efi_info;
|
||||
|
||||
// Search for the RSDP
|
||||
rsdp_t *rp = NULL;
|
||||
#ifdef __x86_64__
|
||||
if (efi_info->loader_signature == EFI64_LOADER_SIGNATURE) {
|
||||
uintptr_t system_table_addr = (uintptr_t)efi_info->sys_tab_hi << 32 | (uintptr_t)efi_info->sys_tab;
|
||||
system_table_addr = map_region(system_table_addr, sizeof(efi64_system_table_t), true);
|
||||
if (system_table_addr != 0) {
|
||||
rp = find_rsdp_in_efi64_system_table((efi64_system_table_t *)system_table_addr);
|
||||
if (rp) rsdp_source = "EFI64 system table";
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (efi_info->loader_signature == EFI32_LOADER_SIGNATURE) {
|
||||
uintptr_t system_table_addr = map_region(efi_info->sys_tab, sizeof(efi32_system_table_t), true);
|
||||
if (system_table_addr != 0) {
|
||||
rp = find_rsdp_in_efi32_system_table((efi32_system_table_t *)system_table_addr);
|
||||
if (rp) rsdp_source = "EFI32 system table";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (rp == NULL) {
|
||||
// Search the BIOS EBDA area.
|
||||
uintptr_t address = *(uint16_t *)0x40E << 4;
|
||||
if (address) {
|
||||
rp = scan_for_rsdp(address, 0x400);
|
||||
if (rp) rsdp_source = "BIOS EBDA";
|
||||
}
|
||||
}
|
||||
if (rp == NULL) {
|
||||
// Search the BIOS reserved area.
|
||||
rp = scan_for_rsdp(0xE0000, 0x20000);
|
||||
if (rp) rsdp_source = "BIOS reserved area";
|
||||
}
|
||||
if (rp == NULL) {
|
||||
// RSDP not found, give up.
|
||||
return 0;
|
||||
}
|
||||
return (uintptr_t)rp;
|
||||
}
|
||||
|
||||
static uintptr_t find_acpi_table(uint32_t table_signature)
|
||||
{
|
||||
rsdp_t *rp = (rsdp_t *)acpi_config.rsdp_addr;
|
||||
|
||||
// Found the RSDP, now get either the RSDT or XSDT
|
||||
// and scan it for a pointer to the table we're looking for
|
||||
rsdt_header_t *rt;
|
||||
|
||||
if (acpi_config.ver_maj < rp->revision) {
|
||||
acpi_config.ver_maj = rp->revision;
|
||||
}
|
||||
|
||||
if (rp->revision >= 2) {
|
||||
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, sizeof(rsdt_header_t), true);
|
||||
if (rt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
// Validate the XSDT.
|
||||
if (*(uint32_t *)rt != XSDTSignature) {
|
||||
return 0;
|
||||
}
|
||||
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, rt->length, true);
|
||||
if (rt == NULL || acpi_checksum(rt, rt->length) != 0) {
|
||||
return 0;
|
||||
}
|
||||
// Scan the XSDT for a pointer to the table we're looking for.
|
||||
uint64_t *tab_ptr = (uint64_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
|
||||
uint64_t *tab_end = (uint64_t *)((uint8_t *)rt + rt->length);
|
||||
|
||||
while (tab_ptr < tab_end) {
|
||||
uintptr_t addr = *tab_ptr++; // read the next table entry
|
||||
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
||||
|
||||
if (ptr && *ptr == table_signature) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, sizeof(rsdt_header_t), true);
|
||||
if (rt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
// Validate the RSDT.
|
||||
if (*(uint32_t *)rt != RSDTSignature) {
|
||||
return 0;
|
||||
}
|
||||
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, rt->length, true);
|
||||
if (rt == NULL || acpi_checksum(rt, rt->length) != 0) {
|
||||
return 0;
|
||||
}
|
||||
// Scan the RSDT for a pointer to the table we're looking for.
|
||||
uint32_t *tab_ptr = (uint32_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
|
||||
uint32_t *tab_end = (uint32_t *)((uint8_t *)rt + rt->length);
|
||||
|
||||
while (tab_ptr < tab_end) {
|
||||
uintptr_t addr = *tab_ptr++; // read the next table entry
|
||||
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
||||
|
||||
if (ptr && *ptr == table_signature) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool parse_fadt(uintptr_t fadt_addr)
|
||||
{
|
||||
// FADT is a very big & complex table and we only need a few data.
|
||||
// We use byte offset instead of a complete struct.
|
||||
|
||||
// FADT Header is identical to RSDP Header
|
||||
rsdt_header_t *fadt = (rsdt_header_t *)fadt_addr;
|
||||
|
||||
// Validate FADT
|
||||
if (fadt == NULL || acpi_checksum(fadt, fadt->length) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get ACPI Version
|
||||
acpi_config.ver_maj = fadt->revision;
|
||||
|
||||
if (fadt->length > FADT_MINOR_REV_OFFSET) {
|
||||
acpi_config.ver_min = *(uint8_t *)(fadt_addr+FADT_MINOR_REV_OFFSET) & 0xF;
|
||||
}
|
||||
|
||||
// Get Old PM Base Address (32bit IO)
|
||||
acpi_config.pm_addr = *(uint32_t *)(fadt_addr+FADT_PM_TMR_BLK_OFFSET);
|
||||
acpi_config.pm_is_io = true;
|
||||
|
||||
#ifdef __x86_64__
|
||||
acpi_gen_addr_struct *rt;
|
||||
|
||||
// Get APIC Timer Address
|
||||
if (fadt->length > FADT_X_PM_TMR_BLK_OFFSET) {
|
||||
rt = (acpi_gen_addr_struct *)map_region(fadt_addr+FADT_X_PM_TMR_BLK_OFFSET, sizeof(acpi_gen_addr_struct), true);
|
||||
|
||||
acpi_config.pm_is_io = (rt->address_space == 1) ? true : false;
|
||||
|
||||
if (rt->address != 0) {
|
||||
acpi_config.pm_addr = rt->address;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int acpi_checksum(const void *data, int length)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
|
||||
uint8_t *ptr = (uint8_t *)data;
|
||||
while (length--) {
|
||||
sum += *ptr++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void acpi_init(void)
|
||||
{
|
||||
acpi_config.rsdp_addr = find_rsdp();
|
||||
|
||||
if (acpi_config.rsdp_addr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acpi_config.madt_addr = find_acpi_table(MADTSignature);
|
||||
|
||||
acpi_config.fadt_addr = find_acpi_table(FADTSignature);
|
||||
|
||||
if (acpi_config.fadt_addr) {
|
||||
parse_fadt(acpi_config.fadt_addr);
|
||||
}
|
||||
|
||||
acpi_config.hpet_addr = find_acpi_table(HPETSignature);
|
||||
}
|
57
system/acpi.h
Normal file
57
system/acpi.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef _ACPI_H_
|
||||
#define _ACPI_H_
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides support for ACPI (Find & parse tables)
|
||||
*
|
||||
*//*
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
* Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define FADT_PM_TMR_BLK_OFFSET 76
|
||||
#define FADT_MINOR_REV_OFFSET 131
|
||||
#define FADT_X_PM_TMR_BLK_OFFSET 208
|
||||
|
||||
/**
|
||||
* A struct containing various ACPI-related infos for later uses.
|
||||
*/
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint8_t ver_maj;
|
||||
uint8_t ver_min;
|
||||
uintptr_t rsdp_addr;
|
||||
uintptr_t madt_addr;
|
||||
uintptr_t fadt_addr;
|
||||
uintptr_t hpet_addr;
|
||||
uintptr_t pm_addr;
|
||||
bool pm_is_io;
|
||||
} acpi_t;
|
||||
|
||||
/**
|
||||
* The search step that located the ACPI RSDP (for debug).
|
||||
*/
|
||||
extern const char *rsdp_source;
|
||||
|
||||
/**
|
||||
* Global ACPI config struct
|
||||
*/
|
||||
extern acpi_t acpi_config;
|
||||
|
||||
/**
|
||||
* ACPI Table Checksum Function
|
||||
*/
|
||||
int acpi_checksum(const void *data, int length);
|
||||
|
||||
/**
|
||||
* Look for specific ACPI Tables Addresses (RSDP, MADT, ...)
|
||||
* and parse some of the tables
|
||||
*/
|
||||
void acpi_init(void);
|
||||
|
||||
#endif /* _ACPI_H_ */
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
// Derived from memtest86+ cpuid.h
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpuid.h"
|
||||
|
@ -36,7 +36,7 @@ void cpuid_init(void)
|
|||
// Get the processor family information & feature flags.
|
||||
if (cpuid_info.max_cpuid >= 1) {
|
||||
cpuid(0x1, 0,
|
||||
&cpuid_info.version.raw,
|
||||
&cpuid_info.version.raw[0],
|
||||
&cpuid_info.proc_info.raw,
|
||||
&cpuid_info.flags.raw[1],
|
||||
&cpuid_info.flags.raw[0]
|
||||
|
@ -65,8 +65,8 @@ void cpuid_init(void)
|
|||
if (cpuid_info.max_xcpuid >= 0x80000001) {
|
||||
cpuid(0x80000001, 0,
|
||||
®[0],
|
||||
&cpuid_info.version.raw[1],
|
||||
®[1],
|
||||
®[2],
|
||||
&cpuid_info.flags.raw[2]
|
||||
);
|
||||
}
|
||||
|
@ -108,9 +108,6 @@ void cpuid_init(void)
|
|||
}
|
||||
|
||||
// Get cache information.
|
||||
switch (cpuid_info.vendor_id.str[0]) {
|
||||
case 'A':
|
||||
// AMD Processors
|
||||
if (cpuid_info.max_xcpuid >= 0x80000005) {
|
||||
cpuid(0x80000005, 0,
|
||||
®[0],
|
||||
|
@ -127,12 +124,6 @@ void cpuid_init(void)
|
|||
&cpuid_info.cache_info.raw[3]
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'G':
|
||||
// Intel Processors
|
||||
// No cpuid info to read.
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect CPU Topology (Core/Thread) infos
|
||||
cpuid_info.topology.core_count = -1;
|
||||
|
@ -166,23 +157,34 @@ void cpuid_init(void)
|
|||
thread_per_core = 2;
|
||||
}
|
||||
} else if (cpuid_info.flags.htt) {
|
||||
if (cpuid_info.version.extendedFamily >= 8) {
|
||||
thread_per_core = 2;
|
||||
} else {
|
||||
cpuid_info.flags.htt = 0; // Pre-ZEN never has SMT
|
||||
}
|
||||
}
|
||||
cpuid_info.topology.core_count = cpuid_info.topology.thread_count / thread_per_core;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
// VIA / CentaurHauls
|
||||
// Cyrix / VIA / CentaurHauls / Zhaoxin
|
||||
cpuid_info.flags.htt = false;
|
||||
break;
|
||||
case 'G':
|
||||
if (cpuid_info.vendor_id.str[7] == 'T') break; // Transmeta
|
||||
// Intel
|
||||
if (cpuid_info.max_cpuid >= 0xB) {
|
||||
cpuid(0xB, 0, ®[0], ®[1], ®[2], ®[3]);
|
||||
}
|
||||
|
||||
if (cpuid_info.max_cpuid >= 0xB && (reg[1] & 0xFF) != 0) { // Check if Extended Topology Information is available
|
||||
|
||||
// Populate Hybrid Status (CPUID 7.EDX[15]) for Alder Lake+
|
||||
cpuid(0x7, 0, ®[0], ®[1], ®[2], ®[3]);
|
||||
if (reg[3] & (1 << 15)) {
|
||||
cpuid_info.topology.is_hybrid = 1;
|
||||
cpuid_info.topology.pcore_count = 1; // We have at least 1 P-Core as BSP
|
||||
cpuid_info.topology.ecore_count = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -209,7 +211,11 @@ void cpuid_init(void)
|
|||
cpuid_info.topology.thread_count = cpuid_info.topology.core_count;
|
||||
|
||||
if (cpuid_info.flags.htt){
|
||||
if (((cpuid_info.proc_info.raw >> 16) & 0xFF) > (uint32_t)cpuid_info.topology.core_count) {
|
||||
cpuid_info.topology.thread_count *= 2;
|
||||
} else {
|
||||
cpuid_info.flags.htt = !cpuid_info.flags.htt;
|
||||
}
|
||||
}
|
||||
} else if (cpuid_info.max_cpuid >= 0x2) {
|
||||
if(cpuid_info.flags.htt){
|
||||
|
@ -222,3 +228,19 @@ void cpuid_init(void)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
core_type_t get_ap_hybrid_type(void)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
cpuid(0x1A, 0, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
switch ((eax >> 24) & 0xFF) {
|
||||
case CPU_PCORE_ID:
|
||||
return CORE_PCORE;
|
||||
case CPU_ECORE_ID:
|
||||
return CORE_ECORE;
|
||||
default:
|
||||
return CORE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,21 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CPU_ECORE_ID 0x20
|
||||
#define CPU_PCORE_ID 0x40
|
||||
|
||||
typedef enum {
|
||||
CORE_UNKNOWN,
|
||||
CORE_PCORE,
|
||||
CORE_ECORE
|
||||
} core_type_t;
|
||||
|
||||
/**
|
||||
* Structures that hold the collected CPUID information.
|
||||
*/
|
||||
|
||||
typedef union {
|
||||
uint32_t raw;
|
||||
uint32_t raw[2];
|
||||
struct {
|
||||
uint32_t stepping : 4;
|
||||
uint32_t model : 4;
|
||||
|
@ -30,6 +39,7 @@ typedef union {
|
|||
uint32_t extendedModel : 4;
|
||||
uint32_t extendedFamily : 8;
|
||||
uint32_t : 4;
|
||||
uint32_t extendedBrandID : 32; // AMD Only
|
||||
};
|
||||
} cpuid_version_t;
|
||||
|
||||
|
@ -113,7 +123,7 @@ typedef union {
|
|||
} cpuid_brand_string_t;
|
||||
|
||||
typedef union {
|
||||
uint32_t raw[12];
|
||||
uint32_t raw[4];
|
||||
struct {
|
||||
uint32_t : 24;
|
||||
uint32_t l1_i_size : 8;
|
||||
|
@ -194,6 +204,11 @@ extern cpuid_info_t cpuid_info;
|
|||
*/
|
||||
void cpuid_init(void);
|
||||
|
||||
/**
|
||||
* Return the Core Type (for Hybrid CPUs)
|
||||
*/
|
||||
core_type_t get_ap_hybrid_type(void);
|
||||
|
||||
/**
|
||||
* Executes the cpuid instruction.
|
||||
*/
|
||||
|
|
203
system/cpuinfo.c
203
system/cpuinfo.c
|
@ -25,6 +25,7 @@
|
|||
#include "pmem.h"
|
||||
#include "vmem.h"
|
||||
#include "memsize.h"
|
||||
#include "hwquirks.h"
|
||||
|
||||
#include "cpuinfo.h"
|
||||
|
||||
|
@ -32,7 +33,6 @@
|
|||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define PIT_TICKS_50mS 59659 // PIT clock is 1.193182MHz
|
||||
#define BENCH_MIN_START_ADR 0x1000000 // 16MB
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -71,12 +71,45 @@ static void determine_cache_size()
|
|||
l3_cache *= 512;
|
||||
break;
|
||||
case 'C':
|
||||
// Zhaoxin CPU only
|
||||
if (cpuid_info.version.family != 7) {
|
||||
if (cpuid_info.vendor_id.str[5] == 'I') {
|
||||
// Cyrix
|
||||
if (cpuid_info.version.family == 5 && cpuid_info.version.model == 4) {
|
||||
// Media GXm, Geode GXm/GXLV/GX1
|
||||
// Cache info in CPUID has a Cyrix-specific encoding so hardcode it
|
||||
l1_cache = 16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// WinChip 2/3, VIA C3/C7/Nano
|
||||
if (cpuid_info.version.family == 5 || cpuid_info.version.family == 6) {
|
||||
l1_cache = cpuid_info.cache_info.l1_d_size;
|
||||
l2_cache = cpuid_info.cache_info.l2_size;
|
||||
break;
|
||||
} else if (cpuid_info.version.family != 7) {
|
||||
break;
|
||||
}
|
||||
// Zhaoxin CPU only
|
||||
/* fall through */
|
||||
case 'G':
|
||||
if (cpuid_info.vendor_id.str[9] == 'N') {
|
||||
// National Semiconductor
|
||||
if (cpuid_info.version.family == 5) {
|
||||
switch (cpuid_info.version.model) {
|
||||
case 4:
|
||||
// Geode GXm/GXLV/GX1
|
||||
// Cache info in CPUID has a Cyrix-specific encoding so hardcode it
|
||||
l1_cache = 16;
|
||||
break;
|
||||
case 5:
|
||||
// Geode GX2
|
||||
l1_cache = cpuid_info.cache_info.l1_d_size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Intel Processors
|
||||
l1_cache = 0;
|
||||
l2_cache = 0;
|
||||
|
@ -298,8 +331,10 @@ static void determine_imc(void)
|
|||
case 0xA:
|
||||
if (cpuid_info.version.extendedModel == 5) {
|
||||
imc_type = IMC_K19_CZN; // AMD Cezanne APU (Model 0x50-5F - Family 19h)
|
||||
} else if (cpuid_info.version.extendedModel >= 6) {
|
||||
imc_type = IMC_K19_RPL; // Zen4 (Family 19h)
|
||||
} else {
|
||||
imc_type = IMC_K19; // Zen3 & Zen4 (Family 19h)
|
||||
imc_type = IMC_K19; // Zen3 (Family 19h)
|
||||
}
|
||||
default:
|
||||
break;
|
||||
|
@ -458,6 +493,9 @@ static void determine_imc(void)
|
|||
case 0x9:
|
||||
imc_type = IMC_KBL; // Core 7/8/9th Gen (Kaby/Coffee/Comet Lake)
|
||||
break;
|
||||
case 0xB:
|
||||
imc_type = IMC_ADL_N; // Core 12th Gen (Alder Lake-N - Gracemont E-Cores only)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -597,9 +635,9 @@ static void determine_cpu_model(void)
|
|||
// Transmeta Processors - vendor_id starts with "GenuineTMx86"
|
||||
if (cpuid_info.vendor_id.str[7] == 'T' ) {
|
||||
if (cpuid_info.version.family == 5) {
|
||||
cpu_model = "TM 5x00";
|
||||
cpu_model = "Transmeta TM 5x00";
|
||||
} else if (cpuid_info.version.family == 15) {
|
||||
cpu_model = "TM 8x00";
|
||||
cpu_model = "Transmeta TM 8x00";
|
||||
}
|
||||
l1_cache = cpuid_info.cache_info.l1_i_size + cpuid_info.cache_info.l1_d_size;
|
||||
l2_cache = cpuid_info.cache_info.l2_size;
|
||||
|
@ -645,14 +683,14 @@ static void determine_cpu_model(void)
|
|||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
cpu_model = "Pentium";
|
||||
cpu_model = "Intel Pentium";
|
||||
if (l1_cache == 0) {
|
||||
l1_cache = 8;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
cpu_model = "Pentium-MMX";
|
||||
cpu_model = "Intel Pentium MMX";
|
||||
if (l1_cache == 0) {
|
||||
l1_cache = 16;
|
||||
}
|
||||
|
@ -665,54 +703,54 @@ static void determine_cpu_model(void)
|
|||
switch (cpuid_info.version.model) {
|
||||
case 0:
|
||||
case 1:
|
||||
cpu_model = "Pentium Pro";
|
||||
cpu_model = "Intel Pentium Pro";
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
cpu_model = "Pentium II";
|
||||
cpu_model = "Intel Pentium II";
|
||||
break;
|
||||
case 5:
|
||||
if (l2_cache == 0) {
|
||||
cpu_model = "Celeron";
|
||||
cpu_model = "Intel Celeron";
|
||||
} else {
|
||||
cpu_model = "Pentium II";
|
||||
cpu_model = "Intel Pentium II";
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (l2_cache == 128) {
|
||||
cpu_model = "Celeron";
|
||||
cpu_model = "Intel Celeron";
|
||||
} else {
|
||||
cpu_model = "Pentium II";
|
||||
cpu_model = "Intel Pentium II";
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 11:
|
||||
if (l2_cache == 128) {
|
||||
cpu_model = "Celeron";
|
||||
cpu_model = "Intel Celeron";
|
||||
} else {
|
||||
cpu_model = "Pentium III";
|
||||
cpu_model = "Intel Pentium III";
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (l2_cache == 512) {
|
||||
cpu_model = "Celeron M (0.13)";
|
||||
cpu_model = "Intel Celeron M (0.13)";
|
||||
} else {
|
||||
cpu_model = "Pentium M (0.13)";
|
||||
cpu_model = "Intel Pentium M (0.13)";
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
cpu_model = "Pentium III Xeon";
|
||||
cpu_model = "Intel Pentium III Xeon";
|
||||
break;
|
||||
case 12:
|
||||
l1_cache = 24;
|
||||
cpu_model = "Atom (0.045)";
|
||||
cpu_model = "Intel Atom (0.045)";
|
||||
break;
|
||||
case 13:
|
||||
if (l2_cache == 1024) {
|
||||
cpu_model = "Celeron M (0.09)";
|
||||
cpu_model = "Intel Celeron M (0.09)";
|
||||
} else {
|
||||
cpu_model = "Pentium M (0.09)";
|
||||
cpu_model = "Intel Pentium M (0.09)";
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
|
@ -720,7 +758,7 @@ static void determine_cpu_model(void)
|
|||
break;
|
||||
case 15:
|
||||
if (l2_cache == 1024) {
|
||||
cpu_model = "Pentium E";
|
||||
cpu_model = "Intel Pentium E";
|
||||
} else {
|
||||
cpu_model = "Intel Core 2";
|
||||
}
|
||||
|
@ -735,17 +773,17 @@ static void determine_cpu_model(void)
|
|||
case 1:
|
||||
case 2:
|
||||
if (l2_cache == 128) {
|
||||
cpu_model = "Celeron";
|
||||
cpu_model = "Intel Celeron";
|
||||
} else {
|
||||
cpu_model = "Pentium 4";
|
||||
cpu_model = "Intel Pentium 4";
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
if (l2_cache == 256) {
|
||||
cpu_model = "Celeron (0.09)";
|
||||
cpu_model = "Intel Celeron (0.09)";
|
||||
} else {
|
||||
cpu_model = "Pentium 4 (0.09)";
|
||||
cpu_model = "Intel Pentium 4 (0.09)";
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
|
@ -765,78 +803,40 @@ static void determine_cpu_model(void)
|
|||
// VIA/Cyrix/Centaur Processors with CPUID
|
||||
if (cpuid_info.vendor_id.str[1] == 'e' ) {
|
||||
// CentaurHauls
|
||||
l1_cache = cpuid_info.cache_info.l1_i_size + cpuid_info.cache_info.l1_d_size;
|
||||
l2_cache = cpuid_info.cache_info.l2_size >> 8;
|
||||
switch (cpuid_info.version.family) {
|
||||
case 5:
|
||||
cpu_model = "Centaur 5x86";
|
||||
break;
|
||||
case 6: // VIA C3
|
||||
switch (cpuid_info.version.model) {
|
||||
case 10:
|
||||
cpu_model = "VIA C7 (C5J)";
|
||||
l1_cache = 64;
|
||||
l2_cache = 128;
|
||||
break;
|
||||
case 13:
|
||||
cpu_model = "VIA C7 (C5R)";
|
||||
l1_cache = 64;
|
||||
l2_cache = 128;
|
||||
break;
|
||||
case 15:
|
||||
cpu_model = "VIA Isaiah (CN)";
|
||||
l1_cache = 64;
|
||||
l2_cache = 128;
|
||||
cpu_model = "IDT WinChip C6";
|
||||
l1_cache = 32;
|
||||
// WinChip 2/3 (models 8/9) have brand string
|
||||
break;
|
||||
default:
|
||||
if (cpuid_info.version.stepping < 8) {
|
||||
cpu_model = "VIA C3 Samuel2";
|
||||
} else {
|
||||
cpu_model = "VIA C3 Eden";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// All VIA/Centaur family values >= 6 have brand string
|
||||
break;
|
||||
}
|
||||
} else { /* CyrixInstead */
|
||||
switch (cpuid_info.version.family) {
|
||||
case 5:
|
||||
case 4:
|
||||
switch (cpuid_info.version.model) {
|
||||
case 0:
|
||||
cpu_model = "Cyrix 6x86MX/MII";
|
||||
case 2:
|
||||
cpu_model = "Cyrix 5x86";
|
||||
l1_cache = 16;
|
||||
break;
|
||||
case 4:
|
||||
cpu_model = "Cyrix GXm";
|
||||
cpu_model = "Cyrix MediaGX/GXi";
|
||||
l1_cache = 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 6: // VIA C3
|
||||
switch (cpuid_info.version.model) {
|
||||
case 5:
|
||||
cpu_model = "Cyrix 6x86/6x86L";
|
||||
l1_cache = 16;
|
||||
// Media GXm (model 4) has brand string
|
||||
break;
|
||||
case 6:
|
||||
cpu_model = "Cyrix III";
|
||||
break;
|
||||
case 7:
|
||||
if (cpuid_info.version.stepping < 8) {
|
||||
cpu_model = "VIA C3 Samuel2";
|
||||
} else {
|
||||
cpu_model = "VIA C3 Ezra-T";
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
cpu_model = "VIA C3 Ezra-T";
|
||||
break;
|
||||
case 9:
|
||||
cpu_model = "VIA C3 Nehemiah";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// L1 = L2 = 64 KB from Cyrix III to Nehemiah
|
||||
cpu_model = "Cyrix 6x86MX/MII";
|
||||
l1_cache = 64;
|
||||
l2_cache = 64;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -847,10 +847,10 @@ static void determine_cpu_model(void)
|
|||
// Unknown processor - make a guess at the family.
|
||||
switch (cpuid_info.version.family) {
|
||||
case 5:
|
||||
cpu_model = "586";
|
||||
cpu_model = "586-class CPU (unknown)";
|
||||
break;
|
||||
case 6:
|
||||
cpu_model = "686";
|
||||
cpu_model = "686-class CPU (unknown)";
|
||||
break;
|
||||
default:
|
||||
cpu_model = "Unidentified Processor";
|
||||
|
@ -860,37 +860,6 @@ static void determine_cpu_model(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void measure_cpu_speed(void)
|
||||
{
|
||||
if (cpuid_info.flags.rdtsc == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up timer
|
||||
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
||||
outb(0xb0, 0x43);
|
||||
outb(PIT_TICKS_50mS & 0xff, 0x42);
|
||||
outb(PIT_TICKS_50mS >> 8, 0x42);
|
||||
|
||||
uint32_t start_time;
|
||||
rdtscl(start_time);
|
||||
|
||||
int loops = 0;
|
||||
do {
|
||||
loops++;
|
||||
} while ((inb(0x61) & 0x20) == 0);
|
||||
|
||||
uint32_t end_time;
|
||||
rdtscl(end_time);
|
||||
|
||||
uint32_t run_time = end_time - start_time;
|
||||
|
||||
// Make sure we have a credible result
|
||||
if (loops >= 4 && run_time >= 50000) {
|
||||
clks_per_msec = run_time / 50;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t memspeed(uintptr_t src, uint32_t len, int iter)
|
||||
{
|
||||
uintptr_t dst;
|
||||
|
@ -1083,12 +1052,14 @@ void cpuinfo_init(void)
|
|||
determine_cache_size();
|
||||
|
||||
determine_cpu_model();
|
||||
|
||||
measure_cpu_speed();
|
||||
}
|
||||
|
||||
void membw_init(void)
|
||||
{
|
||||
if (quirk.type & QUIRK_TYPE_MEM_SIZE) {
|
||||
quirk.process();
|
||||
}
|
||||
|
||||
if(enable_bench) {
|
||||
measure_memory_bandwidth();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*//*
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
* Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
* Copyright (C) 2004-2023 Sam Demeulemeester.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
@ -47,6 +47,7 @@
|
|||
#define IMC_KBL_UY 0x3030 // Core 7/8/9th Gen (Kaby/Coffee/Comet/Amber Lake-U/Y)
|
||||
#define IMC_ICL 0x3040 // Core 10th Gen (IceLake-Y)
|
||||
#define IMC_TGL 0x3050 // Core 11th Gen (Tiger Lake-U)
|
||||
#define IMC_ADL_N 0x3061 // Core 12th Gen (Alder Lake-N - Gracemont E-Cores only)
|
||||
|
||||
#define IMC_BYT 0x4010 // Atom Bay Trail
|
||||
#define IMC_CDT 0x4020 // Atom Cedar Trail
|
||||
|
@ -59,8 +60,9 @@
|
|||
#define IMC_K16 0x8050 // Kabini & related (Family 16h)
|
||||
#define IMC_K17 0x8060 // Zen & Zen2 (Family 17h)
|
||||
#define IMC_K18 0x8070 // Hygon (Family 18h)
|
||||
#define IMC_K19 0x8080 // Zen3 & Zen4(Family 19h)
|
||||
#define IMC_K19 0x8080 // Zen3 (Family 19h)
|
||||
#define IMC_K19_CZN 0x8081 // Cezanne APU
|
||||
#define IMC_K19_RPL 0x8091 // Zen4 (Family 19h)
|
||||
|
||||
/**
|
||||
* A string identifying the CPU make and model.
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "heap.h"
|
||||
#include "memrw32.h"
|
||||
#include "memsize.h"
|
||||
#include "pci.h"
|
||||
#include "pmem.h"
|
||||
#include "usb.h"
|
||||
|
||||
#include "string.h"
|
||||
|
@ -226,11 +226,6 @@ typedef struct {
|
|||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t num_pages(size_t size)
|
||||
{
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static int usb_to_ehci_speed(usb_speed_t usb_speed)
|
||||
{
|
||||
switch (usb_speed) {
|
||||
|
@ -410,10 +405,9 @@ static bool assign_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_
|
|||
usb_speed_t device_speed, int device_id, usb_ep_t *ep0)
|
||||
{
|
||||
// Store the extra information needed by build_ehci_qhd().
|
||||
ep0->driver_data = port_num << 8;
|
||||
if (hub->level > 0) {
|
||||
ep0->driver_data |= hub->ep0->device_id;
|
||||
}
|
||||
usb_parent_t hs_parent = usb_hs_parent(hub, port_num, device_speed);
|
||||
ep0->driver_data = hs_parent.port_num << 8 | hs_parent.device_id;
|
||||
|
||||
if (!assign_usb_address(hcd, hub, port_num, device_speed, device_id, ep0)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -492,7 +486,7 @@ static const hcd_methods_t methods = {
|
|||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool ehci_reset(int bus, int dev, int func, uintptr_t base_addr)
|
||||
{
|
||||
ehci_cap_regs_t *cap_regs = (ehci_cap_regs_t *)base_addr;
|
||||
|
||||
|
@ -519,20 +513,35 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
if (!halt_host_controller(op_regs)) return false;
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ehci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
ehci_cap_regs_t *cap_regs = (ehci_cap_regs_t *)base_addr;
|
||||
|
||||
ehci_op_regs_t *op_regs = (ehci_op_regs_t *)(base_addr + cap_regs->cap_length);
|
||||
|
||||
// Record the heap state to allow us to free memory.
|
||||
uintptr_t initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
// Allocate and initialise a periodic frame list. This needs to be aligned on a 4K page boundary. Some controllers
|
||||
// don't support a programmable list length, so we just use the default length.
|
||||
pm_map[0].end -= num_pages(EHCI_MAX_PFL_LENGTH * sizeof(uint32_t));
|
||||
uintptr_t pfl_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
uintptr_t pfl_addr = heap_alloc(HEAP_TYPE_LM_1, EHCI_MAX_PFL_LENGTH * sizeof(uint32_t), PAGE_SIZE);
|
||||
if (pfl_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
uint32_t *pfl = (uint32_t *)pfl_addr;
|
||||
|
||||
for (int i = 0; i < EHCI_MAX_PFL_LENGTH; i++) {
|
||||
pfl[i] = EHCI_LP_TERMINATE;
|
||||
}
|
||||
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory,
|
||||
// so allocate it in the first segment.
|
||||
// TODO: check for segment overflow.
|
||||
pm_map[0].end -= num_pages(sizeof(workspace_t));
|
||||
uintptr_t workspace_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory.
|
||||
uintptr_t workspace_addr = heap_alloc(HEAP_TYPE_LM_1, sizeof(workspace_t), PAGE_SIZE);
|
||||
if (workspace_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
workspace_t *ws = (workspace_t *)workspace_addr;
|
||||
|
||||
memset(ws, 0, sizeof(workspace_t));
|
||||
|
@ -549,9 +558,7 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
write32(&op_regs->periodic_list_base, pfl_addr);
|
||||
write32(&op_regs->async_list_addr, (uintptr_t)(ws->qhd));
|
||||
if (!start_host_controller(op_regs)) {
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
pm_map[0].end += num_pages(EHCI_MAX_PFL_LENGTH * sizeof(uint32_t));
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
flush32(&op_regs->config_flag, 1);
|
||||
|
||||
|
@ -559,9 +566,8 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
|
||||
// Construct a hub descriptor for the root hub.
|
||||
usb_hub_t root_hub;
|
||||
memset(&root_hub, 0, sizeof(root_hub));
|
||||
root_hub.ep0 = NULL;
|
||||
root_hub.level = 0;
|
||||
root_hub.route = 0;
|
||||
root_hub.num_ports = num_ehci_ports(hcs_params);
|
||||
root_hub.power_up_delay = 10; // 20ms
|
||||
|
||||
|
@ -647,16 +653,8 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
}
|
||||
|
||||
if (num_keyboards == 0) {
|
||||
// Halt the host controller.
|
||||
(void)halt_host_controller(op_regs);
|
||||
|
||||
// Deallocate the workspace for this controller.
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
|
||||
// Deallocate the periodic frame list.
|
||||
pm_map[0].end += num_pages(EHCI_MAX_PFL_LENGTH * sizeof(uint32_t));
|
||||
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
ws->num_keyboards = num_keyboards;
|
||||
|
@ -690,4 +688,8 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
enable_periodic_schedule(op_regs);
|
||||
|
||||
return true;
|
||||
|
||||
no_keyboards_found:
|
||||
heap_rewind(HEAP_TYPE_LM_1, initial_heap_mark);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,35 @@
|
|||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the EHCI device identified by bus, dev, func, and base_addr,
|
||||
* scans all the attached USB devices, and configures any HID USB keyboard
|
||||
* devices it finds to generate periodic interrupt transfers that report key
|
||||
* presses. Initialises hcd and returns true if the device was successfully
|
||||
* initialised and one or more keyboards were found.
|
||||
* If necessary, takes ownership of the EHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param bus - the PCI bus number for accessing the device
|
||||
* \param dev - the PCI device number for accessing the device
|
||||
* \param func - the PCI function number for accessing the device
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool ehci_reset(int bus, int dev, int func, uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the EHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool ehci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // EHCI_H
|
||||
|
|
103
system/heap.c
Normal file
103
system/heap.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2022 Martin Whitaker.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
#include "memsize.h"
|
||||
#include "pmem.h"
|
||||
|
||||
#include "heap.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
int segment;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
} heap_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static heap_t heaps[HEAP_TYPE_LAST] = {
|
||||
{ .segment = -1, .start = 0, .end = 0 },
|
||||
{ .segment = -1, .start = 0, .end = 0 }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t num_pages(size_t size)
|
||||
{
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
uintptr_t heap_alloc(heap_type_t heap_id, size_t size, uintptr_t alignment)
|
||||
{
|
||||
const heap_t * heap = &heaps[heap_id];
|
||||
if (heap->segment < 0) {
|
||||
return 0;
|
||||
}
|
||||
uintptr_t addr = pm_map[heap->segment].end - num_pages(size);
|
||||
addr &= ~((alignment - 1) >> PAGE_SHIFT);
|
||||
if (addr < heap->start) {
|
||||
return 0;
|
||||
}
|
||||
pm_map[heap->segment].end = addr;
|
||||
return addr << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
uintptr_t heap_mark(heap_type_t heap_id)
|
||||
{
|
||||
const heap_t * heap = &heaps[heap_id];
|
||||
if (heap->segment < 0) {
|
||||
return 0;
|
||||
}
|
||||
return pm_map[heap->segment].end;
|
||||
}
|
||||
|
||||
void heap_rewind(heap_type_t heap_id, uintptr_t mark)
|
||||
{
|
||||
const heap_t * heap = &heaps[heap_id];
|
||||
if (heap->segment >= 0 && mark > pm_map[heap->segment].end && mark <= heap->end) {
|
||||
pm_map[heap->segment].end = mark;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void heap_init(void)
|
||||
{
|
||||
// Use the largest 20-bit addressable physical memory segment for the low-memory heap.
|
||||
// Use the largest 32-bit addressable physical memory segment for the high-memory heap.
|
||||
// Exclude memory occupied by the program or below it in that segment.
|
||||
uintptr_t program_start = (uintptr_t)_start >> PAGE_SHIFT;
|
||||
uintptr_t program_end = program_start + num_pages(_end - _start);
|
||||
uintptr_t max_segment_size = 0;
|
||||
for (int i = 0; i < pm_map_size && pm_map[i].end <= PAGE_C(4,GB); i++) {
|
||||
uintptr_t try_heap_start = pm_map[i].start;
|
||||
uintptr_t try_heap_end = pm_map[i].end;
|
||||
if (program_start >= try_heap_start && program_end <= try_heap_end) {
|
||||
try_heap_start = program_end;
|
||||
}
|
||||
uintptr_t segment_size = try_heap_end - try_heap_start;
|
||||
if (segment_size >= max_segment_size) {
|
||||
max_segment_size = segment_size;
|
||||
if (try_heap_end <= PAGE_C(1,MB)) {
|
||||
heaps[HEAP_TYPE_LM_1].segment = i;
|
||||
heaps[HEAP_TYPE_LM_1].start = try_heap_start;
|
||||
heaps[HEAP_TYPE_LM_1].end = try_heap_end;
|
||||
}
|
||||
heaps[HEAP_TYPE_HM_1].segment = i;
|
||||
heaps[HEAP_TYPE_HM_1].start = try_heap_start;
|
||||
heaps[HEAP_TYPE_HM_1].end = try_heap_end;
|
||||
}
|
||||
}
|
||||
}
|
65
system/heap.h
Normal file
65
system/heap.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef HEAP_H
|
||||
#define HEAP_H
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides functions to allocate and free chunks of physical memory that will
|
||||
* be excluded from the memory tests. Two separate heaps are supported, one in
|
||||
* low (20-bit addressable) memory, the other in high (32-bit addressable)
|
||||
* memory.
|
||||
*
|
||||
*//*
|
||||
* Copyright (C) 2022 Martin Whitaker.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
HEAP_TYPE_LM_1,
|
||||
HEAP_TYPE_HM_1,
|
||||
HEAP_TYPE_LAST
|
||||
} heap_type_t;
|
||||
|
||||
/**
|
||||
* Initialises the heaps.
|
||||
*/
|
||||
void heap_init(void);
|
||||
|
||||
/**
|
||||
* Allocates a chunk of physical memory in the given heap. The allocated
|
||||
* region will be at least the requested size with the requested alignment.
|
||||
* This memory is always mapped to the identical address in virtual memory.
|
||||
*
|
||||
* \param heap_id - the target heap.
|
||||
* \param size - the requested size in bytes.
|
||||
* \param alignment - the requested byte alignment (must be a power of 2).
|
||||
*
|
||||
* \returns
|
||||
* On success, the allocated address in physical memory. On failure, 0.
|
||||
*/
|
||||
uintptr_t heap_alloc(heap_type_t heap_id, size_t size, uintptr_t alignment);
|
||||
|
||||
/**
|
||||
* Returns a value indicating the current allocation state of the given
|
||||
* memory heap. This value may be passed to heap_rewind() to free any
|
||||
* memory from that heap allocated after this call.
|
||||
*
|
||||
* \param heap_id - the target heap.
|
||||
*
|
||||
* \returns
|
||||
* An opaque value indicating the current allocation state.
|
||||
*/
|
||||
uintptr_t heap_mark(heap_type_t heap_id);
|
||||
|
||||
/**
|
||||
* Frees any memory allocated in the given heap since the specified mark was
|
||||
* obtained from a call to heap_mark().
|
||||
*
|
||||
* \param heap_id - the target heap.
|
||||
* \param mark - the mark that indicates how much memory to free.
|
||||
*/
|
||||
void heap_rewind(heap_type_t heap_id, uintptr_t mark);
|
||||
|
||||
#endif // HEAP_H
|
202
system/hwquirks.c
Normal file
202
system/hwquirks.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2004-2023 Sam Demeulemeester
|
||||
//
|
||||
// ------------------------
|
||||
// This file is used to detect quirks on specific hardware
|
||||
// that require proprietary init here *OR* different code path
|
||||
// later in various part of the code.
|
||||
//
|
||||
// Please add a quick comment for every quirk added to the list.
|
||||
|
||||
#include "hwquirks.h"
|
||||
#include "io.h"
|
||||
#include "pci.h"
|
||||
#include "unistd.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "cpuid.h"
|
||||
#include "config.h"
|
||||
#include "temperature.h"
|
||||
|
||||
quirk_t quirk;
|
||||
|
||||
// --------------------------------------
|
||||
// -- Private quirk-specific functions --
|
||||
// --------------------------------------
|
||||
|
||||
static void asus_tusl2_configure_mux(void)
|
||||
{
|
||||
uint8_t muxreg;
|
||||
|
||||
// Enter ASB100 Config Mode
|
||||
outb(0x87, 0x2E);
|
||||
outb(0x87, 0x2E);
|
||||
usleep(200);
|
||||
|
||||
// Write LPC Command to access Config Mode Reg
|
||||
lpc_outb(0x7, 0x8);
|
||||
|
||||
// Read Config Mode Register
|
||||
muxreg = lpc_inb(0xF1);
|
||||
|
||||
// Change Smbus Mux Channel & Write Config Mode Register
|
||||
muxreg &= 0xE7;
|
||||
muxreg |= 0x10;
|
||||
lpc_outb(0xF1, muxreg);
|
||||
usleep(200);
|
||||
|
||||
// Leave Config Mode
|
||||
outb(0xAA, 0x2E);
|
||||
}
|
||||
|
||||
static void get_m1541_l2_cache_size(void)
|
||||
{
|
||||
if (l2_cache != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if L2 cache is enabled with L2CC-2 Register[0]
|
||||
if ((pci_config_read8(0, 0, 0, 0x42) & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get L2 Cache Size with L2CC-1 Register[3:2]
|
||||
uint8_t reg = (pci_config_read8(0, 0, 0, 0x41) >> 2) & 3;
|
||||
|
||||
if (reg == 0b00) { l2_cache = 256; }
|
||||
if (reg == 0b01) { l2_cache = 512; }
|
||||
if (reg == 0b10) { l2_cache = 1024; }
|
||||
}
|
||||
|
||||
static void disable_temp_reporting(void)
|
||||
{
|
||||
enable_temperature = false;
|
||||
}
|
||||
|
||||
static void amd_k8_revfg_temp(void)
|
||||
{
|
||||
uint32_t rtcr = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K8);
|
||||
|
||||
// For Rev F & G, switch sensor if no temperature is reported
|
||||
if (!((rtcr >> 16) & 0xFF)) {
|
||||
pci_config_write8(0, 24, 3, AMD_TEMP_REG_K8, rtcr | 0x04);
|
||||
}
|
||||
|
||||
// K8 Rev G Desktop requires an additional offset.
|
||||
if (cpuid_info.version.extendedModel < 6 && cpuid_info.version.extendedModel > 7) // Not Rev G
|
||||
return;
|
||||
|
||||
if (cpuid_info.version.extendedModel == 6 && cpuid_info.version.extendedModel < 9) // Not Desktop
|
||||
return;
|
||||
|
||||
uint16_t brandID = (cpuid_info.version.extendedBrandID >> 9) & 0x1f;
|
||||
|
||||
if (cpuid_info.version.model == 0xF && (brandID == 0x7 || brandID == 0x9 || brandID == 0xC)) // Mobile (Single Core)
|
||||
return;
|
||||
|
||||
if (cpuid_info.version.model == 0xB && brandID > 0xB) // Mobile (Dual Core)
|
||||
return;
|
||||
|
||||
cpu_temp_offset = 21.0f;
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// -- Public function --
|
||||
// ---------------------
|
||||
|
||||
void quirks_init(void)
|
||||
{
|
||||
quirk.id = QUIRK_NONE;
|
||||
quirk.type = QUIRK_TYPE_NONE;
|
||||
quirk.root_vid = pci_config_read16(0, 0, 0, PCI_VID_REG);
|
||||
quirk.root_did = pci_config_read16(0, 0, 0, PCI_DID_REG);
|
||||
quirk.process = NULL;
|
||||
|
||||
// -------------------------
|
||||
// -- ALi Aladdin V Quirk --
|
||||
// -------------------------
|
||||
// As on many Socket 7 Motherboards, the L2 cache is external and must
|
||||
// be detected by a proprietary way based on chipset registers
|
||||
if (quirk.root_vid == PCI_VID_ALI && quirk.root_did == 0x1541) { // ALi Aladdin V (M1541)
|
||||
quirk.id = QUIRK_ALI_ALADDIN_V;
|
||||
quirk.type |= QUIRK_TYPE_MEM_SIZE;
|
||||
quirk.process = get_m1541_l2_cache_size;
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// -- ASUS TUSL2-C Quirk --
|
||||
// ------------------------
|
||||
// This motherboard has an ASB100 ASIC with a SMBUS Mux Integrated.
|
||||
// To access SPD later in the code, we need to configure the mux.
|
||||
// PS: Detection via DMI is unreliable, so using Root PCI Registers
|
||||
if (quirk.root_vid == PCI_VID_INTEL && quirk.root_did == 0x1130) { // Intel i815
|
||||
if (pci_config_read16(0, 0, 0, PCI_SUB_VID_REG) == PCI_VID_ASUS) { // ASUS
|
||||
if (pci_config_read16(0, 0, 0, PCI_SUB_DID_REG) == 0x8027) { // TUSL2-C
|
||||
quirk.id = QUIRK_TUSL2;
|
||||
quirk.type |= QUIRK_TYPE_SMBUS;
|
||||
quirk.process = asus_tusl2_configure_mux;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// -- SuperMicro X10SDV Quirk (GitHub Issue #233) --
|
||||
// -------------------------------------------------
|
||||
// Memtest86+ crashs on Super Micro X10SDV motherboard with SMP Enabled
|
||||
// We were unable to find a solution so far, so disable SMP by default
|
||||
if (quirk.root_vid == PCI_VID_INTEL && quirk.root_did == 0x6F00) { // Broadwell-E (Xeon-D)
|
||||
if (pci_config_read16(0, 0, 0, PCI_SUB_VID_REG) == PCI_VID_SUPERMICRO) { // Super Micro
|
||||
quirk.id = QUIRK_X10SDV_NOSMP;
|
||||
quirk.type |= QUIRK_TYPE_SMP;
|
||||
quirk.process = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// -- Early AMD K8 doesn't support temperature reading --
|
||||
// ------------------------------------------------------
|
||||
// The on-die temperature diode on SH-B0/B3 stepping does not work.
|
||||
if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF
|
||||
&& cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel == 0) { // Early K8
|
||||
if ((cpuid_info.version.model == 4 && cpuid_info.version.stepping == 0) || // SH-B0 ClawHammer (Athlon 64)
|
||||
(cpuid_info.version.model == 5 && cpuid_info.version.stepping <= 1)) { // SH-B0/B3 SledgeHammer (Opteron)
|
||||
quirk.id = QUIRK_K8_BSTEP_NOTEMP;
|
||||
quirk.type |= QUIRK_TYPE_TEMP;
|
||||
quirk.process = disable_temp_reporting;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// -- Late AMD K8 (rev F/G) temp sensor workaround --
|
||||
// ---------------------------------------------------
|
||||
if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF
|
||||
&& cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel >= 4) { // Later K8
|
||||
|
||||
quirk.id = QUIRK_K8_REVFG_TEMP;
|
||||
quirk.type |= QUIRK_TYPE_TEMP;
|
||||
quirk.process = amd_k8_revfg_temp;
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
// -- AMD K10 CPUs Temp workaround (Errata #319) --
|
||||
// ------------------------------------------------
|
||||
// Some AMD K10 CPUs on Socket AM2+/F have buggued thermal diode leading
|
||||
// to inaccurate temperature measurements. Affected steppings: DR-BA/B2/B3, RB-C2 & HY-D0.
|
||||
if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF
|
||||
&& cpuid_info.version.extendedFamily == 1 && cpuid_info.version.extendedModel == 0) { // AMD K10
|
||||
|
||||
uint8_t pkg_type = (cpuid_info.version.extendedBrandID >> 28) & 0x0F;
|
||||
uint32_t dct0_high = pci_config_read32(0, 24, 2, 0x94); // 0x94[8] = 1 for DDR3
|
||||
|
||||
if (pkg_type == 0b0000 || (pkg_type == 0b0001 && (((dct0_high >> 8) & 1) == 0))) { // Socket F or AM2+ (exclude AM3)
|
||||
|
||||
if (cpuid_info.version.model < 4 || // DR-BA, DR-B2 & DR-B3
|
||||
(cpuid_info.version.model == 4 && cpuid_info.version.stepping <= 2) || // RB-C2
|
||||
cpuid_info.version.model == 8) { // HY-D0
|
||||
|
||||
quirk.id = QUIRK_AMD_ERRATA_319;
|
||||
quirk.type |= QUIRK_TYPE_TEMP;
|
||||
quirk.process = disable_temp_reporting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
system/hwquirks.h
Normal file
46
system/hwquirks.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester
|
||||
#ifndef _QUIRK_H_
|
||||
#define _QUIRK_H_
|
||||
/**
|
||||
*
|
||||
* Provides support for hardware quirks
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define QUIRK_TYPE_NONE (1 << 0)
|
||||
#define QUIRK_TYPE_USB (1 << 1)
|
||||
#define QUIRK_TYPE_SMP (1 << 2)
|
||||
#define QUIRK_TYPE_SMBIOS (1 << 3)
|
||||
#define QUIRK_TYPE_SMBUS (1 << 4)
|
||||
#define QUIRK_TYPE_TIMER (1 << 5)
|
||||
#define QUIRK_TYPE_MEM_SIZE (1 << 6)
|
||||
#define QUIRK_TYPE_TEMP (1 << 7)
|
||||
|
||||
typedef enum {
|
||||
QUIRK_NONE,
|
||||
QUIRK_TUSL2,
|
||||
QUIRK_ALI_ALADDIN_V,
|
||||
QUIRK_X10SDV_NOSMP,
|
||||
QUIRK_K8_BSTEP_NOTEMP,
|
||||
QUIRK_K8_REVFG_TEMP,
|
||||
QUIRK_AMD_ERRATA_319
|
||||
} quirk_id_t;
|
||||
|
||||
typedef struct {
|
||||
quirk_id_t id;
|
||||
uint8_t type;
|
||||
uint16_t root_vid;
|
||||
uint16_t root_did;
|
||||
void (*process)(void);
|
||||
} quirk_t;
|
||||
|
||||
extern quirk_t quirk;
|
||||
|
||||
void quirks_init(void);
|
||||
|
||||
#endif /* _QUIRK_H_ */
|
|
@ -79,7 +79,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x003D, "Tektronix" },
|
||||
// { 0x003E, "Oracle Corporation" },
|
||||
// { 0x003F, "Silicon Storage Technology" },
|
||||
// { 0x0040, "ProMos/Mosel Vitelic" },
|
||||
{ 0x0040, "MOSEL" },
|
||||
{ 0x0041, "Infineon" },
|
||||
{ 0x0042, "Macronix" },
|
||||
// { 0x0043, "Xerox" },
|
||||
|
@ -161,7 +161,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0111, "DATARAM" },
|
||||
// { 0x0112, "United Microelectronics Corp" },
|
||||
// { 0x0113, "TCSI" },
|
||||
// { 0x0114, "Smart Modular" },
|
||||
{ 0x0114, "Smart Modular" },
|
||||
// { 0x0115, "Hughes Aircraft" },
|
||||
// { 0x0116, "Lanstar Semiconductor" },
|
||||
// { 0x0117, "Qlogic" },
|
||||
|
@ -392,7 +392,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x027B, "Accelerant Networks" },
|
||||
// { 0x027C, "Silicon Wave" },
|
||||
// { 0x027D, "SandCraft" },
|
||||
// { 0x027E, "Elpida" },
|
||||
{ 0x027E, "Elpida" },
|
||||
// { 0x0301, "Solectron" },
|
||||
// { 0x0302, "Optosys Technologies" },
|
||||
// { 0x0303, "Buffalo (Formerly Melco)" },
|
||||
|
@ -407,14 +407,14 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x030C, "Elite Flash Storage" },
|
||||
// { 0x030D, "Mysticom" },
|
||||
// { 0x030E, "LightSand Communications" },
|
||||
// { 0x030F, "ATI Technologies" },
|
||||
{ 0x030F, "ATI" },
|
||||
// { 0x0310, "Agere Systems" },
|
||||
// { 0x0311, "NeoMagic" },
|
||||
// { 0x0312, "AuroraNetics" },
|
||||
// { 0x0313, "Golden Empire" },
|
||||
{ 0x0313, "GEIL" },
|
||||
{ 0x0314, "Mushkin" },
|
||||
// { 0x0315, "Tioga Technologies" },
|
||||
// { 0x0316, "Netlist" },
|
||||
{ 0x0316, "Netlist" },
|
||||
// { 0x0317, "TeraLogic" },
|
||||
// { 0x0318, "Cicada Semiconductor" },
|
||||
// { 0x0319, "Centon Electronics" },
|
||||
|
@ -478,9 +478,9 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0353, "Primarion" },
|
||||
// { 0x0354, "Picochip Designs Ltd" },
|
||||
// { 0x0355, "Silverback Systems" },
|
||||
// { 0x0356, "Jade Star Technologies" },
|
||||
{ 0x0356, "Jade Star" },
|
||||
// { 0x0357, "Pijnenburg Securealink" },
|
||||
// { 0x0358, "takeMS - Ultron AG" },
|
||||
{ 0x0358, "takeMS" }, // Ultron AG
|
||||
// { 0x0359, "Cambridge Silicon Radio" },
|
||||
{ 0x035A, "Swissbit" },
|
||||
// { 0x035B, "Nazomi Communications" },
|
||||
|
@ -540,7 +540,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0413, "Digital Communications Technology Inc" },
|
||||
// { 0x0414, "Silicon-Based Technology" },
|
||||
// { 0x0415, "Fulcrum Microsystems" },
|
||||
// { 0x0416, "Positivo Informatica Ltd" },
|
||||
{ 0x0416, "Positivo" },
|
||||
// { 0x0417, "XIOtech Corporation" },
|
||||
// { 0x0418, "PortalPlayer" },
|
||||
// { 0x0419, "Zhiying Software" },
|
||||
|
@ -585,7 +585,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0440, "Bandspeed" },
|
||||
// { 0x0441, "LeWiz Communications" },
|
||||
// { 0x0442, "CPU Technology" },
|
||||
// { 0x0443, "Ramaxel Technology" },
|
||||
{ 0x0443, "Ramaxel" },
|
||||
// { 0x0444, "DSP Group" },
|
||||
// { 0x0445, "Axis Communications" },
|
||||
// { 0x0446, "Legacy Electronics" },
|
||||
|
@ -706,7 +706,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x053B, "Solid State System Co Ltd" },
|
||||
// { 0x053C, "Kian Tech LLC" },
|
||||
// { 0x053D, "Artimi" },
|
||||
// { 0x053E, "Power Quotient International" },
|
||||
{ 0x053E, "PQI" },
|
||||
// { 0x053F, "Avago Technologies" },
|
||||
// { 0x0540, "ADTechnology" },
|
||||
// { 0x0541, "Sigma Designs" },
|
||||
|
@ -730,20 +730,20 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0553, "Velogix" },
|
||||
// { 0x0554, "Montalvo Systems" },
|
||||
// { 0x0555, "iVivity Inc" },
|
||||
// { 0x0556, "Walton Chaintech" },
|
||||
// { 0x0557, "AENEON" },
|
||||
{ 0x0556, "Walton Chaintech" },
|
||||
{ 0x0557, "AENEON" },
|
||||
// { 0x0558, "Lorom Industrial Co Ltd" },
|
||||
// { 0x0559, "Radiospire Networks" },
|
||||
// { 0x055A, "Sensio Technologies Inc" },
|
||||
// { 0x055B, "Nethra Imaging" },
|
||||
// { 0x055C, "Hexon Technology Pte Ltd" },
|
||||
{ 0x055C, "Hexon" },
|
||||
// { 0x055D, "CompuStocx (CSX)" },
|
||||
// { 0x055E, "Methode Electronics Inc" },
|
||||
// { 0x055F, "Connect One Ltd" },
|
||||
// { 0x0560, "Opulan Technologies" },
|
||||
// { 0x0561, "Septentrio NV" },
|
||||
// { 0x0562, "Goldenmars Technology Inc" },
|
||||
// { 0x0563, "Kreton Corporation" },
|
||||
{ 0x0562, "Goldenmars" },
|
||||
{ 0x0563, "Kreton Corp." },
|
||||
// { 0x0564, "Cochlear Ltd" },
|
||||
// { 0x0565, "Altair Semiconductor" },
|
||||
// { 0x0566, "NetEffect Inc" },
|
||||
|
@ -835,7 +835,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x063E, "Wilocity" },
|
||||
// { 0x063F, "Novafora Inc" },
|
||||
// { 0x0640, "iKoa Corporation" },
|
||||
// { 0x0641, "ASint Technology" },
|
||||
{ 0x0641, "ASint" },
|
||||
{ 0x0642, "Ramtron" },
|
||||
// { 0x0643, "Plato Networks Inc" },
|
||||
// { 0x0644, "IPtronics AS" },
|
||||
|
@ -874,7 +874,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0666, "Netronome" },
|
||||
// { 0x0667, "Zenverge Inc" },
|
||||
// { 0x0668, "N-trig Ltd" },
|
||||
// { 0x0669, "SanMax Technologies Inc" },
|
||||
{ 0x0669, "SanMax" },
|
||||
// { 0x066A, "Contour Semiconductor Inc" },
|
||||
{ 0x066B, "TwinMOS" },
|
||||
// { 0x066C, "Silicon Systems Inc" },
|
||||
|
@ -882,7 +882,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x066E, "Certicom Corporation" },
|
||||
// { 0x066F, "JSC ICC Milandr" },
|
||||
// { 0x0670, "PhotoFast Global Inc" },
|
||||
// { 0x0671, "InnoDisk Corporation" },
|
||||
{ 0x0671, "InnoDisk" },
|
||||
// { 0x0672, "Muscle Power" },
|
||||
// { 0x0673, "Energy Micro" },
|
||||
// { 0x0674, "Innofidei" },
|
||||
|
@ -987,7 +987,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x075A, "Bestdon Technology Co Ltd" },
|
||||
// { 0x075B, "Baysand Inc" },
|
||||
// { 0x075C, "Uroad Technology Co Ltd" },
|
||||
// { 0x075D, "Wilk Elektronik S.A." },
|
||||
{ 0x075D, "Wilk Elektronik" },
|
||||
// { 0x075E, "AAI" },
|
||||
// { 0x075F, "Harman" },
|
||||
// { 0x0760, "Berg Microelectronics Inc" },
|
||||
|
@ -1038,12 +1038,12 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0810, "Exelis" },
|
||||
// { 0x0811, "Satixfy Ltd" },
|
||||
// { 0x0812, "Galaxy Microsystems Ltd" },
|
||||
// { 0x0813, "Gloway International Co Ltd" },
|
||||
{ 0x0813, "Gloway" },
|
||||
// { 0x0814, "Lab" },
|
||||
// { 0x0815, "Smart Energy Instruments" },
|
||||
// { 0x0816, "Approved Memory Corporation" },
|
||||
// { 0x0817, "Axell Corporation" },
|
||||
// { 0x0818, "Essencore Limited" },
|
||||
{ 0x0818, "KLEVV" },
|
||||
// { 0x0819, "Phytium" },
|
||||
// { 0x081A, "Xi'an UniIC Semiconductors Co Ltd" },
|
||||
// { 0x081B, "Ambiq Micro" },
|
||||
|
@ -1098,7 +1098,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x084C, "Shenzhen Pango Microsystems Co Ltd" },
|
||||
// { 0x084D, "Vasekey" },
|
||||
// { 0x084F, "Eyenix Co Ltd" },
|
||||
// { 0x0850, "Heoriady" },
|
||||
{ 0x0850, "Heoriady" },
|
||||
// { 0x0851, "Accelerated Memory Production Inc" },
|
||||
// { 0x0852, "INVECAS Inc" },
|
||||
// { 0x0853, "AP Memory" },
|
||||
|
@ -1208,7 +1208,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x093F, "Pegasus Semiconductor (Shanghai) Co" },
|
||||
// { 0x0940, "Mythic Inc" },
|
||||
// { 0x0941, "Elmos Semiconductor AG" },
|
||||
// { 0x0942, "Kllisre" },
|
||||
{ 0x0942, "Kllisre" },
|
||||
// { 0x0943, "Shenzhen Winconway Technology" },
|
||||
// { 0x0944, "Shenzhen Xingmem Technology Corp" },
|
||||
// { 0x0945, "Gold Key Technology Co Ltd" },
|
||||
|
@ -1261,7 +1261,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0974, "Hyundai Inc" },
|
||||
// { 0x0975, "EXCELERAM" },
|
||||
// { 0x0976, "PsiKick" },
|
||||
// { 0x0977, "Netac Technology Co Ltd" },
|
||||
{ 0x0977, "Netac" },
|
||||
{ 0x0978, "PCCOOLER" },
|
||||
// { 0x0979, "Jiangsu Huacun Electronic Technology" },
|
||||
// { 0x097A, "Shenzhen Micro Innovation Industry" },
|
||||
|
@ -1335,7 +1335,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0A42, "Thermaltake Technology Co Ltd" },
|
||||
// { 0x0A43, "Shenzhen O?Yang Maile Technology Ltd" },
|
||||
// { 0x0A44, "UPMEM" },
|
||||
// { 0x0A45, "Chun Well Technology Holding Limited" },
|
||||
{ 0x0A45, "Chun Well" },
|
||||
// { 0x0A46, "Astera Labs Inc" },
|
||||
// { 0x0A47, "Winconway" },
|
||||
// { 0x0A48, "Advantech Co Ltd" },
|
||||
|
@ -1384,7 +1384,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0A73, "MCLogic Inc" },
|
||||
// { 0x0A74, "Eorex Corporation" },
|
||||
// { 0x0A75, "Arm Technology (China) Co Ltd" },
|
||||
// { 0x0A76, "Lexar Co Limited" },
|
||||
{ 0x0A76, "Lexar" },
|
||||
// { 0x0A77, "QinetiQ Group plc" },
|
||||
// { 0x0A78, "Exascend" },
|
||||
// { 0x0A79, "Hong Kong Hyunion Electronics Co Ltd" },
|
||||
|
@ -1410,7 +1410,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0B0F, "Quadratica LLC" },
|
||||
// { 0x0B10, "Anpec Electronics" },
|
||||
// { 0x0B11, "Xi'an Morebeck Semiconductor Tech Co" },
|
||||
// { 0x0B12, "Kingbank Technology Co Ltd" },
|
||||
{ 0x0B12, "Kingbank" },
|
||||
// { 0x0B13, "ITRenew Inc" },
|
||||
// { 0x0B14, "Shenzhen Eaget Innovation Tech Ltd" },
|
||||
// { 0x0B15, "Jazer" },
|
||||
|
@ -1556,7 +1556,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0C23, "Tangem AG" },
|
||||
// { 0x0C24, "FuturePath Technology (Shenzhen) Co" },
|
||||
// { 0x0C25, "RC Module" },
|
||||
// { 0x0C26, "Timetec International Inc" },
|
||||
{ 0x0C26, "Timetec" },
|
||||
// { 0x0C27, "ICMAX Technologies Co Limited" },
|
||||
// { 0x0C28, "Lynxi Technologies Ltd Co" },
|
||||
// { 0x0C29, "Guangzhou Taisupanke Computer Equipment" },
|
||||
|
@ -1611,7 +1611,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
|
|||
// { 0x0C5A, "Fraunhofer IPMS" },
|
||||
// { 0x0C5B, "Shenzhen Daxinlang Electronic Tech Co" },
|
||||
// { 0x0C5C, "Abacus Peripherals Private Limited" },
|
||||
// { 0x0C5D, "OLOy Technology" },
|
||||
{ 0x0C5D, "OLOy" },
|
||||
// { 0x0C5E, "Wuhan P&S Semiconductor Co Ltd" },
|
||||
// { 0x0C5F, "Sitrus Technology" },
|
||||
// { 0x0C60, "AnHui Conner Storage Co Ltd" },
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bootparams.h"
|
||||
|
||||
#include "io.h"
|
||||
#include "usbhcd.h"
|
||||
|
||||
|
@ -212,7 +214,7 @@ static const char usb_hid_keymap[] = {
|
|||
// Public Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
keyboard_types_t keyboard_types = KT_LEGACY | KT_USB;
|
||||
keyboard_types_t keyboard_types = KT_NONE;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
|
@ -220,6 +222,16 @@ keyboard_types_t keyboard_types = KT_LEGACY | KT_USB;
|
|||
|
||||
void keyboard_init(void)
|
||||
{
|
||||
if (keyboard_types == KT_NONE) {
|
||||
// No command line option was found, so set the default according to
|
||||
// how we were booted.
|
||||
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
||||
if (boot_params->efi_info.loader_signature != 0) {
|
||||
keyboard_types = KT_USB|KT_LEGACY;
|
||||
} else {
|
||||
keyboard_types = KT_LEGACY;
|
||||
}
|
||||
}
|
||||
if (keyboard_types & KT_USB) {
|
||||
find_usb_keyboards(keyboard_types == KT_USB);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
#define MSR_AMD64_NB_CFG 0xc001001f
|
||||
#define MSR_AMD64_COFVID_STATUS 0xc0010071
|
||||
|
||||
#define MSR_VIA_TEMP_C7 0x1169
|
||||
#define MSR_VIA_TEMP_NANO 0x1423
|
||||
|
||||
#define rdmsr(msr, value1, value2) \
|
||||
__asm__ __volatile__("rdmsr" \
|
||||
: "=a" (value1), \
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "heap.h"
|
||||
#include "memrw32.h"
|
||||
#include "memsize.h"
|
||||
#include "pmem.h"
|
||||
#include "usb.h"
|
||||
|
||||
#include "string.h"
|
||||
|
@ -243,9 +243,34 @@ typedef struct {
|
|||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t num_pages(size_t size)
|
||||
static bool reset_host_controller(ohci_op_regs_t *op_regs)
|
||||
{
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
// Prepare for host controller setup (see section 5.1.1.3 of the OHCI spec.).
|
||||
switch (read32(&op_regs->control) & OHCI_CTRL_HCFS) {
|
||||
case OHCI_CTRL_HCFS_RST:
|
||||
usleep(50*MILLISEC);
|
||||
break;
|
||||
case OHCI_CTRL_HCFS_SUS:
|
||||
case OHCI_CTRL_HCFS_RES:
|
||||
flush32(&op_regs->control, OHCI_CTRL_HCFS_RES);
|
||||
usleep(20*MILLISEC);
|
||||
break;
|
||||
default: // operational
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the host controller.
|
||||
write32(&op_regs->command_status, OHCI_CMD_HCR);
|
||||
if (!wait_until_clr(&op_regs->command_status, OHCI_CMD_HCR, 30)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check we are now in SUSPEND state.
|
||||
if ((read32(&op_regs->control) & OHCI_CTRL_HCFS) != OHCI_CTRL_HCFS_SUS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool reset_ohci_port(ohci_op_regs_t *op_regs, int port_idx)
|
||||
|
@ -411,11 +436,11 @@ static const hcd_methods_t methods = {
|
|||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool ohci_reset(uintptr_t base_addr)
|
||||
{
|
||||
ohci_op_regs_t *op_regs = (ohci_op_regs_t *)base_addr;
|
||||
|
||||
// Check the host controller revison.
|
||||
// Check the host controller revision.
|
||||
if ((read32(&op_regs->revision) & 0xff) != 0x10) {
|
||||
return false;
|
||||
}
|
||||
|
@ -429,6 +454,20 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
}
|
||||
}
|
||||
|
||||
// Reset the controller, but preserve the frame interval set by the SMM or BIOS.
|
||||
uint32_t fm_interval = read32(&op_regs->fm_interval);
|
||||
if (!reset_host_controller(op_regs)) {
|
||||
return false;
|
||||
}
|
||||
write32(&op_regs->fm_interval, fm_interval);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ohci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
ohci_op_regs_t *op_regs = (ohci_op_regs_t *)base_addr;
|
||||
|
||||
// Preserve the frame interval set by the SMM or BIOS.
|
||||
// If not set, use the default value.
|
||||
uint32_t frame_interval = read32(&op_regs->fm_interval) & 0x3fff;
|
||||
|
@ -436,36 +475,21 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
frame_interval = 0x2edf;
|
||||
}
|
||||
|
||||
// Prepare for host controller setup (see section 5.1.1.3 of the OHCI spec.).
|
||||
switch (read32(&op_regs->control) & OHCI_CTRL_HCFS) {
|
||||
case OHCI_CTRL_HCFS_RST:
|
||||
usleep(50*MILLISEC);
|
||||
break;
|
||||
case OHCI_CTRL_HCFS_SUS:
|
||||
case OHCI_CTRL_HCFS_RES:
|
||||
flush32(&op_regs->control, OHCI_CTRL_HCFS_RES);
|
||||
usleep(10*MILLISEC);
|
||||
break;
|
||||
default: // operational
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the host controller.
|
||||
write32(&op_regs->command_status, OHCI_CMD_HCR);
|
||||
if (!wait_until_clr(&op_regs->command_status, OHCI_CMD_HCR, 30)) {
|
||||
// We will have already reset the controller, but can't guarantee to get
|
||||
// here within the 2ms time limit for moving directly from suspend state
|
||||
// to operational state. So reset it again.
|
||||
if (!reset_host_controller(op_regs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check we are now in SUSPEND state.
|
||||
if ((read32(&op_regs->control) & OHCI_CTRL_HCFS) != OHCI_CTRL_HCFS_SUS) {
|
||||
return false;
|
||||
}
|
||||
// Record the heap state to allow us to free memory.
|
||||
uintptr_t initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory,
|
||||
// so allocate it in the first segment.
|
||||
// TODO: check for segment overflow.
|
||||
pm_map[0].end -= num_pages(sizeof(workspace_t));
|
||||
uintptr_t workspace_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory.
|
||||
uintptr_t workspace_addr = heap_alloc(HEAP_TYPE_LM_1, sizeof(workspace_t), PAGE_SIZE);
|
||||
if (workspace_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
workspace_t *ws = (workspace_t *)workspace_addr;
|
||||
|
||||
memset(ws, 0, sizeof(workspace_t));
|
||||
|
@ -500,9 +524,8 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
|
||||
// Construct a hub descriptor for the root hub.
|
||||
usb_hub_t root_hub;
|
||||
memset(&root_hub, 0, sizeof(root_hub));
|
||||
root_hub.ep0 = NULL;
|
||||
root_hub.level = 0;
|
||||
root_hub.route = 0;
|
||||
root_hub.num_ports = rh_descriptor_a & 0xf;
|
||||
root_hub.power_up_delay = rh_descriptor_a >> 24;
|
||||
|
||||
|
@ -574,10 +597,7 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
// Delay to allow the controller to reset.
|
||||
usleep(10);
|
||||
|
||||
// Deallocate the workspace for this controller.
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
|
||||
|
@ -611,4 +631,8 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
flush32(&op_regs->interrupt_status, ~0);
|
||||
|
||||
return true;
|
||||
|
||||
no_keyboards_found:
|
||||
heap_rewind(HEAP_TYPE_LM_1, initial_heap_mark);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,32 @@
|
|||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the OHCI device found at base_addr, scans all the attached USB
|
||||
* devices, and configures any HID USB keyboard devices it finds to generate
|
||||
* periodic interrupt transfers that report key presses. Initialises hcd and
|
||||
* returns true if the device was successfully initialised and one or more
|
||||
* keyboards were found.
|
||||
* If necessary, takes ownership of the OHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool ohci_reset(uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the OHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool ohci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // OHCI_H
|
||||
|
|
25
system/pci.c
25
system/pci.c
|
@ -22,6 +22,7 @@
|
|||
#include "io.h"
|
||||
|
||||
#include "pci.h"
|
||||
#include "unistd.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
|
@ -244,6 +245,30 @@ void pci_config_write32(int bus, int dev, int func, int reg, uint32_t value)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------
|
||||
// LPC Functions
|
||||
// -------------
|
||||
|
||||
void lpc_outb(uint8_t cmd, uint8_t data)
|
||||
{
|
||||
outb(cmd, 0x2E);
|
||||
usleep(100);
|
||||
outb(data, 0x2F);
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
uint8_t lpc_inb(uint8_t reg)
|
||||
{
|
||||
outb(reg, 0x2E);
|
||||
usleep(100);
|
||||
return inb(0x2F);
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// AMD System Management Network Functions
|
||||
// ---------------------------------------
|
||||
|
||||
uint32_t amd_smn_read(uint32_t adr)
|
||||
{
|
||||
pci_config_write32(0, 0, 0, 0x60, adr);
|
||||
|
|
26
system/pci.h
26
system/pci.h
|
@ -12,6 +12,25 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PCI_VID_REG 0x00
|
||||
#define PCI_DID_REG 0x02
|
||||
#define PCI_SUB_VID_REG 0x2C
|
||||
#define PCI_SUB_DID_REG 0x2E
|
||||
|
||||
/* Vendor IDs */
|
||||
#define PCI_VID_ATI 0x1002
|
||||
#define PCI_VID_AMD 0x1022
|
||||
#define PCI_VID_SIS 0x1039
|
||||
#define PCI_VID_ASUS 0x1043
|
||||
#define PCI_VID_EFAR 0x1055
|
||||
#define PCI_VID_ALI 0x10B9
|
||||
#define PCI_VID_NVIDIA 0x10DE
|
||||
#define PCI_VID_VIA 0x1106
|
||||
#define PCI_VID_SERVERWORKS 0x1166
|
||||
#define PCI_VID_SUPERMICRO 0x15D9
|
||||
#define PCI_VID_HYGON 0x1D94
|
||||
#define PCI_VID_INTEL 0x8086
|
||||
|
||||
#define PCI_MAX_BUS 256
|
||||
#define PCI_MAX_DEV 32
|
||||
#define PCI_MAX_FUNC 8
|
||||
|
@ -60,6 +79,13 @@ void pci_config_write16(int bus, int dev, int func, int reg, uint16_t value);
|
|||
void pci_config_write32(int bus, int dev, int func, int reg, uint32_t value);
|
||||
|
||||
|
||||
/**
|
||||
* Basic LPC Functions
|
||||
*/
|
||||
|
||||
void lpc_outb(uint8_t cmd, uint8_t data);
|
||||
uint8_t lpc_inb(uint8_t reg);
|
||||
|
||||
/*
|
||||
* Add some SNM related function (S.DEMEULEMEESTER)
|
||||
*/
|
||||
|
|
|
@ -42,7 +42,7 @@ static const rgb_value_t vga_pallete[16] = {
|
|||
{ 255, 255, 255 } // BOLD+WHITE
|
||||
};
|
||||
|
||||
static vga_buffer_t *vga_buffer = (vga_buffer_t *)(0xb8000);
|
||||
static vga_buffer_t *vga_buffer = NULL;
|
||||
|
||||
vga_buffer_t shadow_buffer;
|
||||
|
||||
|
@ -64,8 +64,10 @@ static void vga_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|||
shadow_buffer[row][col].ch = ch;
|
||||
shadow_buffer[row][col].attr = attr;
|
||||
|
||||
if (vga_buffer) {
|
||||
(*vga_buffer)[row][col].value = shadow_buffer[row][col].value;
|
||||
}
|
||||
}
|
||||
|
||||
static void lfb8_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
||||
{
|
||||
|
@ -239,6 +241,8 @@ void screen_init(void)
|
|||
uint32_t b = ((vga_pallete[i].b * b_max) / 255) << screen_info->blue_pos;
|
||||
lfb_pallete[i] = r | g | b;
|
||||
}
|
||||
} else if (screen_info->orig_video_isVGA != VIDEO_TYPE_NONE) {
|
||||
vga_buffer = (vga_buffer_t *)(0xb8000);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,33 +67,24 @@ void serial_echo_print(const char *p)
|
|||
if (!port->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now, do each character */
|
||||
while (*p) {
|
||||
serial_wait_for_xmit(port);
|
||||
|
||||
/* Send the character out. */
|
||||
serial_write_reg(port, UART_TX, *p);
|
||||
if (*p==10) {
|
||||
serial_wait_for_xmit(port);
|
||||
serial_write_reg(port, UART_TX, 13);
|
||||
}
|
||||
p++;
|
||||
serial_write_reg(port, UART_TX, *p++);
|
||||
}
|
||||
}
|
||||
|
||||
void tty_print(int y, int x, const char *p)
|
||||
void tty_goto(int y, int x)
|
||||
{
|
||||
static char sx[3], sy[3];
|
||||
|
||||
itoa(++x,sx);
|
||||
itoa(++y,sy);
|
||||
static char s[3];
|
||||
|
||||
serial_echo_print("\x1b[");
|
||||
serial_echo_print(sy);
|
||||
serial_echo_print(itoa(y + 1, s));
|
||||
serial_echo_print(";");
|
||||
serial_echo_print(sx);
|
||||
serial_echo_print(itoa(x + 1, s));
|
||||
serial_echo_print("H");
|
||||
serial_echo_print(p);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -146,7 +137,9 @@ void tty_init(void)
|
|||
void tty_send_region(int start_row, int start_col, int end_row, int end_col)
|
||||
{
|
||||
char p[SCREEN_WIDTH+1];
|
||||
int col_len = end_col - start_col;
|
||||
uint8_t ch;
|
||||
int pos = 0;
|
||||
int cur_inverse = -1, inverse = false;
|
||||
|
||||
if (start_col > (SCREEN_WIDTH - 1) || end_col > (SCREEN_WIDTH - 1)) {
|
||||
return;
|
||||
|
@ -158,42 +151,64 @@ void tty_send_region(int start_row, int start_col, int end_row, int end_col)
|
|||
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
|
||||
// Last line is inverted (Black on white)
|
||||
if (row == SCREEN_HEIGHT-1) {
|
||||
tty_inverse();
|
||||
}
|
||||
// Always use absolute positioning instead of relying on CR-LF to avoid issues
|
||||
// when a CR-LF is lost (especially with Industrial RS232/Ethernet converters).
|
||||
tty_goto(row, start_col);
|
||||
|
||||
// Copy Shadow buffer to TTY buffer
|
||||
pos = 0;
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
p[col] = shadow_buffer[row][col].value & 0x7F;
|
||||
|
||||
inverse = ((shadow_buffer[row][col].attr & 0x70) >> 4 != BLUE);
|
||||
|
||||
if (cur_inverse != inverse) {
|
||||
|
||||
if (pos) {
|
||||
p[pos] = '\0';
|
||||
serial_echo_print(p);
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
// Add string terminator
|
||||
p[end_col+1] = '\0';
|
||||
|
||||
// For first line, title on top-left must be inverted
|
||||
// Do the switch, send to TTY then continue to next line.
|
||||
if (row == 0 && start_col == 0 && col_len > 28) {
|
||||
if (inverse) {
|
||||
tty_inverse();
|
||||
p[28] = '\0';
|
||||
tty_print(row,0,p);
|
||||
} else {
|
||||
tty_normal();
|
||||
p[28] = '|';
|
||||
tty_print(row, 28, p + 28);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace degree symbol with '*' for tty to avoid a C437/VT100 translation table.
|
||||
if (row == 1 && (shadow_buffer[1][25].value & 0xFF) == 0xF8) {
|
||||
p[25] = 0x2A;
|
||||
cur_inverse = inverse;
|
||||
}
|
||||
|
||||
// Send row to TTY
|
||||
tty_print(row, start_col, p + start_col);
|
||||
/* Make sure only VT100 characters are sent. */
|
||||
ch = shadow_buffer[row][col].ch;
|
||||
|
||||
// Revert to normal if last line.
|
||||
if (row == SCREEN_HEIGHT-1) {
|
||||
tty_normal();
|
||||
switch (ch) {
|
||||
case 32 ... 127:
|
||||
break;
|
||||
|
||||
case 0xB3:
|
||||
ch = '|';
|
||||
break;
|
||||
|
||||
case 0xC1:
|
||||
case 0xC2:
|
||||
case 0xC4:
|
||||
ch = '-';
|
||||
break;
|
||||
|
||||
case 0xF8:
|
||||
ch = '*';
|
||||
break;
|
||||
|
||||
default:
|
||||
ch = '?';
|
||||
}
|
||||
|
||||
p[pos++] = ch;
|
||||
}
|
||||
|
||||
if (pos) {
|
||||
p[pos] = '\0';
|
||||
serial_echo_print(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include "stdint.h"
|
||||
#include "string.h"
|
||||
#include "display.h"
|
||||
static const uint8_t * table_start = NULL;
|
||||
static uint32_t table_length = 0; // 16-bit in SMBIOS v2, 32-bit in SMBIOS v3.
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
|
@ -16,6 +14,9 @@ static uint32_t table_length = 0; // 16-bit in SMBIOS v2, 32-bit in SMBIOS v3.
|
|||
|
||||
#define LINE_DMI 23
|
||||
|
||||
static const uint8_t *table_start = NULL;
|
||||
static uint32_t table_length = 0; // 16-bit in SMBIOS v2, 32-bit in SMBIOS v3.
|
||||
|
||||
static const efi_guid_t SMBIOS2_GUID = { 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
||||
|
||||
// SMBIOS v3 compliant FW must include an SMBIOS v2 table, but maybe parse SM3 table later...
|
||||
|
@ -25,7 +26,8 @@ struct system_info *dmi_system_info;
|
|||
struct baseboard_info *dmi_baseboard_info;
|
||||
struct mem_dev *dmi_memory_device;
|
||||
|
||||
static char * get_tstruct_string(struct tstruct_header * header, uint16_t maxlen, int n) {
|
||||
static char *get_tstruct_string(struct tstruct_header *header, uint16_t maxlen, int n)
|
||||
{
|
||||
if (n < 1)
|
||||
return NULL;
|
||||
char *a = (char *) header + header->length;
|
||||
|
@ -41,7 +43,8 @@ static char * get_tstruct_string(struct tstruct_header * header, uint16_t maxlen
|
|||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static smbiosv2_t * find_smbiosv2_in_efi64_system_table(efi64_system_table_t * system_table) {
|
||||
static smbiosv2_t *find_smbiosv2_in_efi64_system_table(efi64_system_table_t *system_table)
|
||||
{
|
||||
efi64_config_table_t *config_tables = (efi64_config_table_t *) map_region(system_table->config_tables, system_table->num_config_tables * sizeof(efi64_config_table_t), true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
|
@ -55,7 +58,8 @@ static smbiosv2_t * find_smbiosv2_in_efi64_system_table(efi64_system_table_t * s
|
|||
}
|
||||
#endif
|
||||
|
||||
static smbiosv2_t * find_smbiosv2_in_efi32_system_table(efi32_system_table_t * system_table) {
|
||||
static smbiosv2_t *find_smbiosv2_in_efi32_system_table(efi32_system_table_t *system_table)
|
||||
{
|
||||
efi32_config_table_t *config_tables = (efi32_config_table_t *) map_region(system_table->config_tables, system_table->num_config_tables * sizeof(efi32_config_table_t), true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
|
@ -68,7 +72,8 @@ static smbiosv2_t * find_smbiosv2_in_efi32_system_table(efi32_system_table_t * s
|
|||
return (smbiosv2_t *) table_addr;
|
||||
}
|
||||
|
||||
static uintptr_t find_smbiosv2_adr(void) {
|
||||
static uintptr_t find_smbiosv2_adr(void)
|
||||
{
|
||||
const boot_params_t *boot_params = (boot_params_t *) boot_params_addr;
|
||||
const efi_info_t *efi_info = & boot_params->efi_info;
|
||||
|
||||
|
@ -112,16 +117,18 @@ static uintptr_t find_smbiosv2_adr(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_dmi(uint16_t numstructs) {
|
||||
static int parse_dmi(uint16_t numstructs)
|
||||
{
|
||||
const uint8_t *dmi = table_start;
|
||||
int tstruct_count = 0;
|
||||
|
||||
// Struct type 1 is one of the mandatory types, so we're dealing with invalid data if its size is lower than that of a minimal type 1 struct (plus a couple bytes).
|
||||
// Struct type 1 is one of the mandatory types, so we're dealing with invalid data
|
||||
// if its size is lower than that of a minimal type 1 struct (plus a couple bytes).
|
||||
if (table_length < sizeof(struct system_info)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse all structs (currently restricted to Type 2 only)
|
||||
// Parse structs
|
||||
while (dmi < table_start + table_length - 2) { // -2 for header type and length.
|
||||
const struct tstruct_header *header = (struct tstruct_header *) dmi;
|
||||
|
||||
|
@ -137,8 +144,13 @@ static int parse_dmi(uint16_t numstructs) {
|
|||
}
|
||||
// Type 17 - Memory Device
|
||||
else if (header->type == 17 && header->length > offsetof(struct mem_dev, partnum)) {
|
||||
// Multiple type 17 structs are allowed, with unpopulated slots sometimes
|
||||
// reported as type 2 (unknown). If type is 0 (uninitialized) or 1/2 (previously
|
||||
// initialized with unknown value) => set or overwrite the struct
|
||||
if (dmi_memory_device->type <= 2) {
|
||||
dmi_memory_device = (struct mem_dev *) dmi;
|
||||
}
|
||||
}
|
||||
|
||||
dmi += header->length;
|
||||
|
||||
|
@ -160,11 +172,11 @@ static int parse_dmi(uint16_t numstructs) {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smbios_init(void) {
|
||||
int smbios_init(void)
|
||||
{
|
||||
uintptr_t smb_adr;
|
||||
const uint8_t *dmi_start;
|
||||
const smbiosv2_t *eps;
|
||||
|
@ -202,21 +214,30 @@ int smbios_init(void) {
|
|||
return parse_dmi(eps->numstructs);
|
||||
}
|
||||
|
||||
void print_smbios_startup_info(void) {
|
||||
// Use baseboard info (struct type 2) as primary source of information, and fall back to system info (struct type 1).
|
||||
// Indeed, while the latter may contain less useful information than the former, its presence is mandated by the successive revisions of the SMBIOS standard.
|
||||
// NOTE: we can get away with this ugly cast because the offsets of .manufacturer and .productname are the same in system_info and baseboard_info.
|
||||
struct system_info * ptr = dmi_baseboard_info != NULL ? (struct system_info *)dmi_baseboard_info : dmi_system_info;
|
||||
void print_smbios_startup_info(void)
|
||||
{
|
||||
// Use baseboard info (struct type 2) as primary source of information,
|
||||
// and fall back to system info (struct type 1). Indeed, while the later
|
||||
// may contain less useful information than the former, its presence is
|
||||
// mandated by the successive revisions of the SMBIOS standard.
|
||||
// NOTE: we can get away with this ugly cast because the offsets of
|
||||
// .manufacturer and .productname are the same in system_info and baseboard_info.
|
||||
|
||||
struct system_info *ptr = dmi_baseboard_info != NULL ?
|
||||
(struct system_info *)dmi_baseboard_info : dmi_system_info;
|
||||
|
||||
if (ptr != NULL) {
|
||||
char *sys_man, *sys_sku;
|
||||
|
||||
int sl1, sl2, dmicol;
|
||||
|
||||
sys_man = get_tstruct_string(&ptr->header, table_length - ((uint8_t *)&ptr->header - (uint8_t *)table_start), ptr->manufacturer);
|
||||
uint16_t struct_length = table_length - ((uint8_t *)&ptr->header - (uint8_t *)table_start);
|
||||
|
||||
sys_man = get_tstruct_string(&ptr->header, struct_length, ptr->manufacturer);
|
||||
if (sys_man != NULL) {
|
||||
sl1 = strlen(sys_man);
|
||||
|
||||
sys_sku = get_tstruct_string(&ptr->header, table_length - ((uint8_t *)&ptr->header - (uint8_t *)table_start), ptr->productname);
|
||||
sys_sku = get_tstruct_string(&ptr->header, struct_length, ptr->productname);
|
||||
if (sys_sku != NULL) {
|
||||
sl2 = strlen(sys_sku);
|
||||
|
||||
|
|
1627
system/smbus.c
1627
system/smbus.c
File diff suppressed because it is too large
Load diff
|
@ -7,12 +7,14 @@
|
|||
*
|
||||
* Provides functions for reading SPD via SMBUS
|
||||
*
|
||||
* Copyright (C) 2004-2022 Samuel Demeulemeester.
|
||||
* Copyright (C) 2004-2023 Sam Demeulemeester.
|
||||
*/
|
||||
|
||||
#define I2C_WRITE 0
|
||||
#define I2C_READ 1
|
||||
|
||||
#define SPD5_MR11 11
|
||||
|
||||
/* i801 Hosts Addresses */
|
||||
#define SMBHSTSTS smbusbase
|
||||
#define SMBHSTCNT smbusbase + 2
|
||||
|
@ -47,41 +49,99 @@
|
|||
/* AMD-Specific constants */
|
||||
#define AMD_INDEX_IO_PORT 0xCD6
|
||||
#define AMD_DATA_IO_PORT 0xCD7
|
||||
#define AMD_SMBUS_BASE_REG 0x2C
|
||||
#define AMD_PM_INDEX 0x00
|
||||
|
||||
#define SPD5_MR11 11
|
||||
/* nVidia-Specific constants */
|
||||
#define NV_SMBUS_ADR_REG 0x20
|
||||
#define NV_OLD_SMBUS_ADR_REG 0x50
|
||||
|
||||
#define NVSMBCNT smbusbase + 0
|
||||
#define NVSMBSTS smbusbase + 1
|
||||
#define NVSMBADD smbusbase + 2
|
||||
#define NVSMBCMD smbusbase + 3
|
||||
#define NVSMBDAT(x) (smbusbase + 4 + (x))
|
||||
|
||||
#define NVSMBCNT_WRITE 0x00
|
||||
#define NVSMBCNT_READ 0x01
|
||||
#define NVSMBCNT_QUICK 0x02
|
||||
#define NVSMBCNT_BYTE 0x04
|
||||
#define NVSMBCNT_BYTE_DATA 0x06
|
||||
#define NVSMBCNT_WORD_DATA 0x08
|
||||
|
||||
#define NVSMBSTS_DONE 0x80
|
||||
#define NVSMBSTS_ALRM 0x40
|
||||
#define NVSMBSTS_RES 0x20
|
||||
#define NVSMBSTS_STATUS 0x1f
|
||||
|
||||
/* ALi-Specific constants (M1563 & newer) */
|
||||
#define ALI_SMBHSTCNT_SIZEMASK 0x03
|
||||
#define ALI_SMBHSTSTS_BAD 0x1C
|
||||
|
||||
#define ALI_SMBHSTCNT_QUICK 0x00
|
||||
#define ALI_SMBHSTCNT_BYTE 0x01
|
||||
#define ALI_SMBHSTCNT_BYTE_DATA 0x02
|
||||
#define ALI_SMBHSTCNT_WORD_DATA 0x03
|
||||
#define ALI_SMBHSTCNT_KILL 0x04
|
||||
#define ALI_SMBHSTCNT_BLOCK 0x05
|
||||
|
||||
/* ALi-Specific constants (M1543 & older) */
|
||||
#define ALI_OLD_SMBHSTSTS_BAD 0xE0
|
||||
#define ALI_OLD_SMBHSTSTS_BUSY 0x08
|
||||
#define ALI_OLD_SMBHSTCNT_BYTE_DATA 0x20
|
||||
|
||||
#define ALI_OLD_SMBHSTCNT smbusbase + 1
|
||||
#define ALI_OLD_SMBHSTSTART smbusbase + 2
|
||||
#define ALI_OLD_SMBHSTADD smbusbase + 3
|
||||
#define ALI_OLD_SMBHSTDAT0 smbusbase + 4
|
||||
#define ALI_OLD_SMBHSTCMD smbusbase + 7
|
||||
|
||||
/** Rounding factors for timing computation
|
||||
*
|
||||
* These factors are used as a configurable CEIL() function
|
||||
* to get the upper int from a float past a specific decimal point.
|
||||
*/
|
||||
|
||||
#define DDR5_ROUNDING_FACTOR 30
|
||||
#define ROUNDING_FACTOR 0.9f
|
||||
|
||||
#define SPD_SKU_LEN 32
|
||||
|
||||
#define PIIX4_SMB_BASE_ADR_DEFAULT 0x90
|
||||
#define PIIX4_SMB_BASE_ADR_VIAPRO 0xD0
|
||||
#define PIIX4_SMB_BASE_ADR_ALI1563 0x80
|
||||
#define PIIX4_SMB_BASE_ADR_ALI1543 0x14
|
||||
|
||||
struct pci_smbus_controller {
|
||||
unsigned vendor;
|
||||
unsigned device;
|
||||
char *name;
|
||||
void (*get_adr)(void);
|
||||
uint8_t (*read_spd_byte)(uint8_t dimmadr, uint16_t bytenum);
|
||||
};
|
||||
|
||||
typedef struct spd_infos {
|
||||
bool isValid;
|
||||
uint32_t module_size;
|
||||
uint8_t slot_num;
|
||||
uint16_t jedec_code;
|
||||
char sku[32];
|
||||
uint8_t sku_len;
|
||||
uint16_t freq;
|
||||
uint32_t module_size;
|
||||
char *type;
|
||||
char sku[SPD_SKU_LEN + 1];
|
||||
uint8_t XMP;
|
||||
uint16_t freq;
|
||||
bool hasECC;
|
||||
uint8_t fab_year;
|
||||
uint8_t fab_week;
|
||||
uint16_t tCL;
|
||||
uint8_t tCL_dec;
|
||||
uint16_t tRCD;
|
||||
uint16_t tRP;
|
||||
uint16_t tRAS;
|
||||
uint16_t tRC;
|
||||
char *type;
|
||||
} spd_info;
|
||||
|
||||
typedef struct ram_infos {
|
||||
uint16_t freq;
|
||||
uint16_t tCL;
|
||||
uint8_t tCL_dec;
|
||||
uint16_t tRCD;
|
||||
uint16_t tRP;
|
||||
uint16_t tRAS;
|
||||
|
@ -90,9 +150,6 @@ typedef struct ram_infos {
|
|||
|
||||
extern ram_info ram;
|
||||
|
||||
#define get_spd(smb_idx, slot_idx, spd_adr) \
|
||||
smbcontrollers[smb_idx].read_spd_byte(slot_idx, spd_adr)
|
||||
|
||||
/**
|
||||
* Print SMBUS Info
|
||||
*/
|
||||
|
|
247
system/smp.c
247
system/smp.c
|
@ -1,11 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
//
|
||||
// Derived from an extract of memtest86+ smp.c:
|
||||
//
|
||||
// MemTest86+ V5 Specific code (GPL V2.0)
|
||||
// By Samuel DEMEULEMEESTER, sdemeule@memtest.org
|
||||
// http://www.canardpc.com - http://www.memtest.org
|
||||
// ------------------------------------------------
|
||||
// smp.c - MemTest-86 Version 3.5
|
||||
//
|
||||
|
@ -15,15 +14,17 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
#include "efi.h"
|
||||
|
||||
#include "cpuid.h"
|
||||
#include "heap.h"
|
||||
#include "hwquirks.h"
|
||||
#include "memrw32.h"
|
||||
#include "memsize.h"
|
||||
#include "msr.h"
|
||||
#include "pmem.h"
|
||||
#include "string.h"
|
||||
#include "unistd.h"
|
||||
#include "vmem.h"
|
||||
|
@ -75,18 +76,8 @@
|
|||
// Table signatures
|
||||
|
||||
#define FPSignature ('_' | ('M' << 8) | ('P' << 16) | ('_' << 24))
|
||||
|
||||
#define MPCSignature ('P' | ('C' << 8) | ('M' << 16) | ('P' << 24))
|
||||
|
||||
#define RSDPSignature1 ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24))
|
||||
#define RSDPSignature2 ('P' | ('T' << 8) | ('R' << 16) | (' ' << 24))
|
||||
|
||||
#define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24))
|
||||
|
||||
#define XSDTSignature ('X' | ('S' << 8) | ('D' << 16) | ('T' << 24))
|
||||
|
||||
#define MADTSignature ('A' | ('P' << 8) | ('I' << 16) | ('C' << 24))
|
||||
|
||||
// MP config table entry types
|
||||
|
||||
#define MP_PROCESSOR 0
|
||||
|
@ -189,30 +180,6 @@ typedef struct {
|
|||
uint8_t dst_apic_lint;
|
||||
} mp_local_interrupt_entry_t;
|
||||
|
||||
typedef struct {
|
||||
char signature[8]; // "RSD PTR "
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_addr;
|
||||
uint32_t length;
|
||||
uint64_t xsdt_addr;
|
||||
uint8_t xchecksum;
|
||||
uint8_t reserved[3];
|
||||
} rsdp_t;
|
||||
|
||||
typedef struct {
|
||||
char signature[4]; // "RSDT" or "XSDT"
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table_id[8];
|
||||
char oem_revision[4];
|
||||
char creator_id[4];
|
||||
char creator_revision[4];
|
||||
} rsdt_header_t;
|
||||
|
||||
typedef struct {
|
||||
char signature[4]; // "APIC"
|
||||
uint32_t length;
|
||||
|
@ -251,9 +218,6 @@ typedef struct {
|
|||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const efi_guid_t EFI_ACPI_1_RDSP_GUID = { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
||||
static const efi_guid_t EFI_ACPI_2_RDSP_GUID = { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81} };
|
||||
|
||||
static apic_register_t *apic = NULL;
|
||||
|
||||
static uint8_t apic_id_to_cpu_num[MAX_APIC_IDS];
|
||||
|
@ -270,10 +234,6 @@ static uintptr_t alloc_addr = 0;
|
|||
|
||||
int num_available_cpus = 1; // There is always at least one CPU, the BSP
|
||||
|
||||
const char *rsdp_source = "";
|
||||
|
||||
uintptr_t rsdp_addr = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -293,24 +253,13 @@ static uint32_t apic_read(int reg)
|
|||
return read32(&apic[reg][0]);
|
||||
}
|
||||
|
||||
static int checksum(const void *data, int length)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
|
||||
uint8_t *ptr = (uint8_t *)data;
|
||||
while (length--) {
|
||||
sum += *ptr++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static floating_pointer_struct_t *scan_for_floating_ptr_struct(uintptr_t addr, int length)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)addr;
|
||||
uint32_t *end = ptr + length / sizeof(uint32_t);
|
||||
|
||||
while (ptr < end) {
|
||||
if (*ptr == FPSignature && checksum(ptr, 16) == 0) {
|
||||
if (*ptr == FPSignature && acpi_checksum(ptr, 16) == 0) {
|
||||
floating_pointer_struct_t *fp = (floating_pointer_struct_t *)ptr;
|
||||
if (fp->length == 1 && (fp->spec_rev == 1 || fp->spec_rev == 4)) {
|
||||
return fp;
|
||||
|
@ -329,7 +278,7 @@ static bool read_mp_config_table(uintptr_t addr)
|
|||
mpc = (mp_config_table_header_t *)map_region(addr, mpc->length, true);
|
||||
if (mpc == NULL) return false;
|
||||
|
||||
if (mpc->signature != MPCSignature || checksum(mpc, mpc->length) != 0) {
|
||||
if (mpc->signature != MPCSignature || acpi_checksum(mpc, mpc->length) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -426,32 +375,19 @@ static bool find_cpus_in_floating_mp_struct(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
|
||||
static bool find_cpus_in_madt(void)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)addr;
|
||||
uint32_t *end = ptr + length / sizeof(uint32_t);
|
||||
|
||||
while (ptr < end) {
|
||||
rsdp_t *rp = (rsdp_t *)ptr;
|
||||
if (*ptr == RSDPSignature1 && *(ptr+1) == RSDPSignature2 && checksum(ptr, 20) == 0) {
|
||||
if (rp->revision < 2 || (rp->length < 1024 && checksum(ptr, rp->length) == 0)) {
|
||||
return rp;
|
||||
}
|
||||
}
|
||||
ptr += 4;
|
||||
}
|
||||
return NULL;
|
||||
if (acpi_config.madt_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse_madt(uintptr_t addr)
|
||||
{
|
||||
madt_table_header_t *mpc = (madt_table_header_t *)map_region(addr, sizeof(madt_table_header_t), true);
|
||||
madt_table_header_t *mpc = (madt_table_header_t *)map_region(acpi_config.madt_addr, sizeof(madt_table_header_t), true);
|
||||
if (mpc == NULL) return false;
|
||||
|
||||
mpc = (madt_table_header_t *)map_region(addr, mpc->length, true);
|
||||
mpc = (madt_table_header_t *)map_region(acpi_config.madt_addr, mpc->length, true);
|
||||
if (mpc == NULL) return false;
|
||||
|
||||
if (checksum(mpc, mpc->length) != 0) {
|
||||
if (acpi_checksum(mpc, mpc->length) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -491,150 +427,6 @@ static bool parse_madt(uintptr_t addr)
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static rsdp_t *find_rsdp_in_efi64_system_table(efi64_system_table_t *system_table)
|
||||
{
|
||||
efi64_config_table_t *config_tables = (efi64_config_table_t *)map_region(system_table->config_tables, system_table->num_config_tables * sizeof(efi64_config_table_t), true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
uintptr_t table_addr = 0;
|
||||
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
break;
|
||||
}
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
}
|
||||
}
|
||||
return (rsdp_t *)table_addr;
|
||||
}
|
||||
#else
|
||||
static rsdp_t *find_rsdp_in_efi32_system_table(efi32_system_table_t *system_table)
|
||||
{
|
||||
efi32_config_table_t *config_tables = (efi32_config_table_t *)map_region(system_table->config_tables, system_table->num_config_tables * sizeof(efi32_config_table_t), true);
|
||||
if (config_tables == NULL) return NULL;
|
||||
|
||||
uintptr_t table_addr = 0;
|
||||
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
break;
|
||||
}
|
||||
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
||||
table_addr = config_tables[i].table;
|
||||
}
|
||||
}
|
||||
return (rsdp_t *)table_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool find_cpus_in_rsdp(void)
|
||||
{
|
||||
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
||||
|
||||
const efi_info_t *efi_info = &boot_params->efi_info;
|
||||
|
||||
// Search for the RSDP
|
||||
rsdp_t *rp = NULL;
|
||||
#ifdef __x86_64__
|
||||
if (efi_info->loader_signature == EFI64_LOADER_SIGNATURE) {
|
||||
uintptr_t system_table_addr = (uintptr_t)efi_info->sys_tab_hi << 32 | (uintptr_t)efi_info->sys_tab;
|
||||
system_table_addr = map_region(system_table_addr, sizeof(efi64_system_table_t), true);
|
||||
if (system_table_addr != 0) {
|
||||
rp = find_rsdp_in_efi64_system_table((efi64_system_table_t *)system_table_addr);
|
||||
if (rp) rsdp_source = "EFI64 system table";
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (efi_info->loader_signature == EFI32_LOADER_SIGNATURE) {
|
||||
uintptr_t system_table_addr = map_region(efi_info->sys_tab, sizeof(efi32_system_table_t), true);
|
||||
if (system_table_addr != 0) {
|
||||
rp = find_rsdp_in_efi32_system_table((efi32_system_table_t *)system_table_addr);
|
||||
if (rp) rsdp_source = "EFI32 system table";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (rp == NULL) {
|
||||
// Search the BIOS EBDA area.
|
||||
uintptr_t address = *(uint16_t *)0x40E << 4;
|
||||
if (address) {
|
||||
rp = scan_for_rsdp(address, 0x400);
|
||||
if (rp) rsdp_source = "BIOS EBDA";
|
||||
}
|
||||
}
|
||||
if (rp == NULL) {
|
||||
// Search the BIOS reserved area.
|
||||
rp = scan_for_rsdp(0xE0000, 0x20000);
|
||||
if (rp) rsdp_source = "BIOS reserved area";
|
||||
}
|
||||
if (rp == NULL) {
|
||||
// RSDP not found, give up.
|
||||
return false;
|
||||
}
|
||||
rsdp_addr = (uintptr_t)rp;
|
||||
|
||||
// Found the RSDP, now get either the RSDT or XSDT and scan it for a pointer to the MADT.
|
||||
rsdt_header_t *rt;
|
||||
if (rp->revision >= 2) {
|
||||
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, sizeof(rsdt_header_t), true);
|
||||
if (rt == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Validate the XSDT.
|
||||
if (*(uint32_t *)rt != XSDTSignature) {
|
||||
return false;
|
||||
}
|
||||
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, rt->length, true);
|
||||
if (rt == NULL || checksum(rt, rt->length) != 0) {
|
||||
return false;
|
||||
}
|
||||
// Scan the XSDT for a pointer to the MADT.
|
||||
uint64_t *tab_ptr = (uint64_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
|
||||
uint64_t *tab_end = (uint64_t *)((uint8_t *)rt + rt->length);
|
||||
|
||||
while (tab_ptr < tab_end) {
|
||||
uintptr_t addr = *tab_ptr++; // read the next table entry
|
||||
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
||||
|
||||
if (ptr && *ptr == MADTSignature) {
|
||||
if (parse_madt(addr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, sizeof(rsdt_header_t), true);
|
||||
if (rt == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Validate the RSDT.
|
||||
if (*(uint32_t *)rt != RSDTSignature) {
|
||||
return false;
|
||||
}
|
||||
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, rt->length, true);
|
||||
if (rt == NULL || checksum(rt, rt->length) != 0) {
|
||||
return false;
|
||||
}
|
||||
// Scan the RSDT for a pointer to the MADT.
|
||||
uint32_t *tab_ptr = (uint32_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
|
||||
uint32_t *tab_end = (uint32_t *)((uint8_t *)rt + rt->length);
|
||||
|
||||
while (tab_ptr < tab_end) {
|
||||
uintptr_t addr = *tab_ptr++; // read the next table entry
|
||||
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
||||
|
||||
if (ptr && *ptr == MADTSignature) {
|
||||
if (parse_madt(addr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void send_ipi(int apic_id, int trigger, int level, int mode, uint8_t vector)
|
||||
{
|
||||
apic_write(APIC_REG_ICRHI, apic_id << 24);
|
||||
|
@ -671,7 +463,8 @@ static uint32_t read_apic_esr(bool is_p5)
|
|||
|
||||
static bool start_cpu(int cpu_num)
|
||||
{
|
||||
// This is based on the method used in Linux 5.14. We don't support non-integrated APICs, so can simplify it a bit.
|
||||
// This is based on the method used in Linux 5.14.
|
||||
// We don't support non-integrated APICs, so can simplify it a bit.
|
||||
|
||||
int apic_id = cpu_num_to_apic_id[cpu_num];
|
||||
|
||||
|
@ -747,8 +540,14 @@ void smp_init(bool smp_enable)
|
|||
}
|
||||
}
|
||||
|
||||
// Process SMP Quirks
|
||||
if (quirk.type & QUIRK_TYPE_SMP) {
|
||||
// quirk.process();
|
||||
smp_enable = false;
|
||||
}
|
||||
|
||||
if (smp_enable) {
|
||||
(void)(find_cpus_in_rsdp() || find_cpus_in_floating_mp_struct());
|
||||
(void)(find_cpus_in_madt() || find_cpus_in_floating_mp_struct());
|
||||
|
||||
}
|
||||
|
||||
|
@ -756,9 +555,9 @@ void smp_init(bool smp_enable)
|
|||
apic_id_to_cpu_num[cpu_num_to_apic_id[i]] = i;
|
||||
}
|
||||
|
||||
// Reserve last page of first segment for AP trampoline and sync objects.
|
||||
// Allocate a page of low memory for AP trampoline and sync objects.
|
||||
// These need to remain pinned in place during relocation.
|
||||
smp_heap_page = --pm_map[0].end;
|
||||
smp_heap_page = heap_alloc(HEAP_TYPE_LM_1, PAGE_SIZE, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
ap_startup_addr = (uintptr_t)startup;
|
||||
|
||||
|
|
|
@ -38,15 +38,6 @@ typedef enum __attribute__ ((packed)) {
|
|||
*/
|
||||
extern int num_available_cpus;
|
||||
|
||||
/**
|
||||
* The search step that located the ACPI RSDP (for debug).
|
||||
*/
|
||||
extern const char *rsdp_source;
|
||||
/**
|
||||
* The address of the ACPI RSDP (for debug).
|
||||
*/
|
||||
extern uintptr_t rsdp_addr;
|
||||
|
||||
/**
|
||||
* Initialises the SMP state and detects the number of available CPU cores.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2023 Sam Demeulemeester.
|
||||
//
|
||||
// Derived from an extract of memtest86+ init.c:
|
||||
//
|
||||
|
@ -16,60 +17,120 @@
|
|||
|
||||
#include "cpuid.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "hwquirks.h"
|
||||
#include "msr.h"
|
||||
#include "pci.h"
|
||||
|
||||
#include "temperature.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
float cpu_temp_offset = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int TjMax = 0;
|
||||
|
||||
void get_specific_TjMax(void)
|
||||
{
|
||||
// The TjMax value for some Mobile/Embedded CPUs must be read from a fixed
|
||||
// table according to their CPUID, PCI Root DID/VID or PNS.
|
||||
// Trying to read the MSR 0x1A2 on some of them trigger a reboot.
|
||||
|
||||
// Yonah C0 Step (Pentium/Core Duo T2000 & Celeron M 200/400)
|
||||
if (cpuid_info.version.raw[0] == 0x6E8) {
|
||||
TjMax = 100;
|
||||
}
|
||||
}
|
||||
|
||||
void temperature_init(void)
|
||||
{
|
||||
uint32_t regl, regh;
|
||||
|
||||
// Process temperature-related quirks
|
||||
if (quirk.type & QUIRK_TYPE_TEMP) {
|
||||
quirk.process();
|
||||
}
|
||||
|
||||
// Get TjMax for Intel CPU
|
||||
if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.max_cpuid >= 6 && (cpuid_info.dts_pmp & 1)) {
|
||||
|
||||
get_specific_TjMax();
|
||||
|
||||
if (TjMax == 0) {
|
||||
// Generic Method using MSR 0x1A2
|
||||
rdmsr(MSR_IA32_TEMPERATURE_TARGET, regl, regh);
|
||||
TjMax = (regl >> 16) & 0x7F;
|
||||
|
||||
if (TjMax < 50 || TjMax > 125) {
|
||||
TjMax = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_cpu_temperature(void)
|
||||
{
|
||||
if (imc_type == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t regl, regh;
|
||||
|
||||
// Intel CPU
|
||||
if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.max_cpuid >= 6) {
|
||||
if (cpuid_info.dts_pmp & 1) {
|
||||
uint32_t msrl, msrh;
|
||||
if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.max_cpuid >= 6 && (cpuid_info.dts_pmp & 1)) {
|
||||
|
||||
rdmsr(MSR_IA32_THERM_STATUS, msrl, msrh);
|
||||
int Tabs = (msrl >> 16) & 0x7F;
|
||||
rdmsr(MSR_IA32_THERM_STATUS, regl, regh);
|
||||
int Tabs = (regl >> 16) & 0x7F;
|
||||
|
||||
rdmsr(MSR_IA32_TEMPERATURE_TARGET, msrl, msrh);
|
||||
int Tjunc = (msrl >> 16) & 0x7F;
|
||||
|
||||
if (Tjunc < 50 || Tjunc > 125) {
|
||||
Tjunc = 90;
|
||||
}
|
||||
return Tjunc - Tabs;
|
||||
}
|
||||
return TjMax - Tabs;
|
||||
}
|
||||
|
||||
// AMD CPU
|
||||
if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.extendedFamily > 0 && cpuid_info.version.extendedFamily < 8) {
|
||||
else if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF) { // Target only K8 & newer
|
||||
|
||||
// Untested yet
|
||||
uint32_t rtcr = pci_config_read32(0, 24, 3, 0xA4);
|
||||
int raw_temp = (rtcr >> 21) & 0x7FF;
|
||||
if (cpuid_info.version.extendedFamily >= 8) { // Target Zen µarch and newer. Use SMN to get temperature.
|
||||
|
||||
return raw_temp / 8;
|
||||
regl = amd_smn_read(SMN_THM_TCON_CUR_TMP);
|
||||
|
||||
} else if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.extendedFamily >= 8) {
|
||||
|
||||
// Grab CPU Temp. for ZEN CPUs using SNM
|
||||
uint32_t tval = amd_smn_read(SMN_THM_TCON_CUR_TMP);
|
||||
|
||||
float offset = 0;
|
||||
|
||||
if((tval >> 19) & 0x01) {
|
||||
offset = -49.0f;
|
||||
if ((regl >> 19) & 0x01) {
|
||||
cpu_temp_offset = -49.0f;
|
||||
}
|
||||
|
||||
return offset + 0.125f * (float)((tval >> 21) & 0x7FF);
|
||||
return cpu_temp_offset + 0.125f * (float)((regl >> 21) & 0x7FF);
|
||||
|
||||
} else if (cpuid_info.version.extendedFamily > 0) { // Target K10 to K15 (Bulldozer)
|
||||
|
||||
regl = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K10);
|
||||
int raw_temp = ((regl >> 21) & 0x7FF) / 8;
|
||||
|
||||
return (raw_temp > 0) ? raw_temp : 0;
|
||||
|
||||
} else { // Target K8 (CPUID ExtFamily = 0)
|
||||
|
||||
regl = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K8);
|
||||
int raw_temp = ((regl >> 16) & 0xFF) - 49 + cpu_temp_offset;
|
||||
|
||||
return (raw_temp > 0) ? raw_temp : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// VIA/Centaur/Zhaoxin CPU
|
||||
else if (cpuid_info.vendor_id.str[0] == 'C' && cpuid_info.vendor_id.str[1] == 'e'
|
||||
&& (cpuid_info.version.family == 6 || cpuid_info.version.family == 7)) {
|
||||
|
||||
uint32_t msr_temp;
|
||||
|
||||
if (cpuid_info.version.family == 7 || cpuid_info.version.model == 0xF) {
|
||||
msr_temp = MSR_VIA_TEMP_NANO; // Zhaoxin, Nano
|
||||
} else if (cpuid_info.version.model == 0xA || cpuid_info.version.model == 0xD) {
|
||||
msr_temp = MSR_VIA_TEMP_C7; // C7 A/D
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rdmsr(msr_temp, regl, regh);
|
||||
return (int)(regl & 0xffffff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -8,8 +8,22 @@
|
|||
*
|
||||
*//*
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
* Copyright (C) 2003-2023 Sam Demeulemeester.
|
||||
*/
|
||||
|
||||
#define AMD_TEMP_REG_K8 0xE4
|
||||
#define AMD_TEMP_REG_K10 0xA4
|
||||
|
||||
/**
|
||||
* Global CPU Temperature offset
|
||||
*/
|
||||
extern float cpu_temp_offset;
|
||||
|
||||
/**
|
||||
* Init temperature sensor and compute offsets if needed
|
||||
*/
|
||||
void temperature_init(void);
|
||||
|
||||
/**
|
||||
* Returns the current temperature of the CPU. Returns 0 if
|
||||
* the temperature cannot be read.
|
||||
|
|
96
system/timers.c
Normal file
96
system/timers.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "cpuid.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "io.h"
|
||||
#include "tsc.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define PIT_TICKS_50mS 59659 // PIT clock is 1.193182MHz
|
||||
#define APIC_TICKS_50mS 178977 // APIC clock is 3.579545MHz
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void correct_tsc(void)
|
||||
{
|
||||
uint32_t start_time, end_time, run_time, counter;
|
||||
int loops = 0;
|
||||
|
||||
if (cpuid_info.flags.rdtsc == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If available, use APIC Timer to find TSC correction factor
|
||||
if (acpi_config.pm_is_io && acpi_config.pm_addr != 0) {
|
||||
rdtscl(start_time);
|
||||
|
||||
counter = inl(acpi_config.pm_addr);
|
||||
|
||||
// Generate a dirty delay
|
||||
for(volatile uint8_t i=0; i<100u; i++);
|
||||
|
||||
// Make sure counter is incrementing
|
||||
if (inl(acpi_config.pm_addr) > counter) {
|
||||
|
||||
while (1) {
|
||||
if (inl(acpi_config.pm_addr) > (counter + APIC_TICKS_50mS) || loops > 1000000) {
|
||||
break;
|
||||
}
|
||||
loops++;
|
||||
}
|
||||
|
||||
rdtscl(end_time);
|
||||
|
||||
run_time = end_time - start_time;
|
||||
|
||||
// Make sure we have a credible result
|
||||
if (loops >= 10 && run_time >= 50000) {
|
||||
clks_per_msec = run_time / 50;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use PIT Timer to find TSC correction factor if APIC not available
|
||||
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
||||
outb(0xb0, 0x43);
|
||||
outb(PIT_TICKS_50mS & 0xff, 0x42);
|
||||
outb(PIT_TICKS_50mS >> 8, 0x42);
|
||||
|
||||
rdtscl(start_time);
|
||||
|
||||
loops = 0;
|
||||
do {
|
||||
loops++;
|
||||
} while ((inb(0x61) & 0x20) == 0);
|
||||
|
||||
rdtscl(end_time);
|
||||
|
||||
run_time = end_time - start_time;
|
||||
|
||||
// Make sure we have a credible result
|
||||
if (loops >= 4 && run_time >= 50000) {
|
||||
clks_per_msec = run_time / 50;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void timers_init(void)
|
||||
{
|
||||
correct_tsc();
|
||||
}
|
20
system/timers.h
Normal file
20
system/timers.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef _TIMERS_H_
|
||||
#define _TIMERS_H_
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides support for various timers sources
|
||||
*
|
||||
*//*
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
* Copyright (C) 2004-2022 Sam Demeulemeester
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Initialize timers (to correct TSC frequency)
|
||||
*/
|
||||
void timers_init(void);
|
||||
|
||||
#endif /* _TIMERS_H_ */
|
|
@ -5,11 +5,11 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "heap.h"
|
||||
#include "io.h"
|
||||
#include "memrw32.h"
|
||||
#include "memsize.h"
|
||||
#include "pci.h"
|
||||
#include "pmem.h"
|
||||
#include "usb.h"
|
||||
|
||||
#include "string.h"
|
||||
|
@ -195,11 +195,6 @@ typedef struct {
|
|||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t num_pages(size_t size)
|
||||
{
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static bool io_wait_until_clr(uint16_t io_reg, uint16_t bit_mask, int max_time)
|
||||
{
|
||||
int timer = max_time >> 3;
|
||||
|
@ -425,7 +420,7 @@ static const hcd_methods_t methods = {
|
|||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
||||
bool uhci_reset(int bus, int dev, int func, uint16_t io_base)
|
||||
{
|
||||
// Disable PCI and SMM interrupts.
|
||||
pci_config_write16(bus, dev, func, UHCI_LEGSUP, UHCI_LEGSUP_CLEAR);
|
||||
|
@ -434,16 +429,26 @@ bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
|||
if (!halt_host_controller(io_base)) return false;
|
||||
if (!reset_host_controller(io_base)) return false;
|
||||
|
||||
// Allocate and initialise the frame list. This needs to be aligned on a 4K page boundary.
|
||||
pm_map[0].end -= num_pages(UHCI_FL_LENGTH * sizeof(uint32_t));
|
||||
uintptr_t fl_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhci_probe(uint16_t io_base, usb_hcd_t *hcd)
|
||||
{
|
||||
// Record the heap state to allow us to free memory.
|
||||
uintptr_t initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
// Allocate the frame list. This needs to be aligned on a 4K page boundary.
|
||||
uintptr_t fl_addr = heap_alloc(HEAP_TYPE_LM_1, UHCI_FL_LENGTH * sizeof(uint32_t), PAGE_SIZE);
|
||||
if (fl_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
uint32_t *fl = (uint32_t *)fl_addr;
|
||||
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory,
|
||||
// so allocate it in the first segment.
|
||||
// TODO: check for segment overflow.
|
||||
pm_map[0].end -= num_pages(sizeof(workspace_t));
|
||||
uintptr_t workspace_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory.
|
||||
uintptr_t workspace_addr = heap_alloc(HEAP_TYPE_LM_1, sizeof(workspace_t), PAGE_SIZE);
|
||||
if (workspace_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
workspace_t *ws = (workspace_t *)workspace_addr;
|
||||
|
||||
memset(ws, 0, sizeof(workspace_t));
|
||||
|
@ -469,18 +474,14 @@ bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
|||
outl(fl_addr, UHCI_FLBASE);
|
||||
outb(UHCI_SOF_DEFAULT, UHCI_SOF);
|
||||
if (!start_host_controller(io_base)) {
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
pm_map[0].end += num_pages(UHCI_FL_LENGTH * sizeof(uint32_t));
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
// Construct a hub descriptor for the root hub.
|
||||
usb_hub_t root_hub;
|
||||
memset(&root_hub, 0, sizeof(root_hub));
|
||||
root_hub.ep0 = NULL;
|
||||
root_hub.level = 0;
|
||||
root_hub.route = 0;
|
||||
root_hub.num_ports = MAX_UHCI_PORTS;
|
||||
root_hub.power_up_delay = 0;
|
||||
|
||||
usleep(100*MILLISEC); // USB maximum device attach time
|
||||
|
||||
|
@ -535,16 +536,8 @@ bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
|||
num_keyboards, num_keyboards != 1 ? "s" : "");
|
||||
|
||||
if (num_keyboards == 0) {
|
||||
// Halt the host controller.
|
||||
(void)halt_host_controller(io_base);
|
||||
|
||||
// Deallocate the workspace for this controller.
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
|
||||
// Deallocate the periodic frame list.
|
||||
pm_map[0].end += num_pages(UHCI_FL_LENGTH * sizeof(uint32_t));
|
||||
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
ws->num_keyboards = num_keyboards;
|
||||
|
@ -582,4 +575,8 @@ bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
|||
}
|
||||
|
||||
return true;
|
||||
|
||||
no_keyboards_found:
|
||||
heap_rewind(HEAP_TYPE_LM_1, initial_heap_mark);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,35 @@
|
|||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the UHCI device found at bus, dev, func on the PCI bus and
|
||||
* io_base in the I/O address space, scans all the attached USB devices, and
|
||||
* configures any HID USB keyboard devices it finds to generate periodic
|
||||
* interrupt transfers that report key presses. Initialises hcd and returns
|
||||
* true if the device was successfully initialised and one or more keyboards
|
||||
* were found.
|
||||
* If necessary, takes ownership of the UHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param bus - the PCI bus number for accessing the device
|
||||
* \param dev - the PCI device number for accessing the device
|
||||
* \param func - the PCI function number for accessing the device
|
||||
* \param io_base - the base address of the device in I/O space
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd);
|
||||
bool uhci_reset(int bus, int dev, int func, uint16_t io_base);
|
||||
|
||||
/**
|
||||
* Initialises the UHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param io_base - the base address of the device in I/O space
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool uhci_probe(uint16_t io_base, usb_hcd_t *hcd);
|
||||
|
||||
#endif // UHCI_H
|
||||
|
|
225
system/usbhcd.c
225
system/usbhcd.c
|
@ -22,7 +22,9 @@
|
|||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MAX_USB_CONTROLLERS 8 // an arbitrary limit - must match the initialisation of usb_controllers
|
||||
#define MAX_HCI 16 // an arbitrary limit - only affects stack usage
|
||||
|
||||
#define MAX_HCD 8 // an arbitrary limit - must match the initialisation of hcd_list
|
||||
|
||||
#define PAUSE_IF_NONE_TIME 10 // seconds
|
||||
|
||||
|
@ -41,6 +43,15 @@ typedef enum {
|
|||
MAX_HCI_TYPE = 4
|
||||
} hci_type_t;
|
||||
|
||||
typedef struct {
|
||||
hci_type_t type;
|
||||
uint8_t bus;
|
||||
uint8_t dev;
|
||||
uint8_t func;
|
||||
uintptr_t pm_base_addr;
|
||||
uintptr_t vm_base_addr;
|
||||
} hci_info_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -60,7 +71,7 @@ static const hcd_methods_t methods = {
|
|||
};
|
||||
|
||||
// All entries in this array must be initialised in order to generate the necessary relocation records.
|
||||
static usb_hcd_t usb_controllers[MAX_USB_CONTROLLERS] = {
|
||||
static usb_hcd_t hcd_list[MAX_HCD] = {
|
||||
{ &methods, NULL },
|
||||
{ &methods, NULL },
|
||||
{ &methods, NULL },
|
||||
|
@ -71,7 +82,7 @@ static usb_hcd_t usb_controllers[MAX_USB_CONTROLLERS] = {
|
|||
{ &methods, NULL }
|
||||
};
|
||||
|
||||
static int num_usb_controllers = 0;
|
||||
static int num_hcd = 0;
|
||||
|
||||
static int print_row = 0;
|
||||
static int print_col = 0;
|
||||
|
@ -136,6 +147,8 @@ static bool build_hub_info(const usb_hcd_t *hcd, const usb_hub_t *parent, int po
|
|||
hub->num_ports = hub_desc.num_ports;
|
||||
hub->tt_think_time = hub_desc.characteristics & 0x0060 >> 5;
|
||||
hub->power_up_delay = hub_desc.power_up_delay;
|
||||
hub->hs_parent = usb_hs_parent(parent, port_num, ep0->device_speed);
|
||||
|
||||
usb_endpoint_desc_t *ep1_desc = find_hub_endpoint_descriptor(hcd->ws->data_buffer, hcd->ws->data_length);
|
||||
if (ep1_desc == NULL) {
|
||||
return false;
|
||||
|
@ -347,8 +360,56 @@ static bool scan_hub_ports(const usb_hcd_t *hcd, const usb_hub_t *hub, int *num_
|
|||
return keyboard_found;
|
||||
}
|
||||
|
||||
static void probe_usb_controller(int bus, int dev, int func, hci_type_t controller_type)
|
||||
static int find_usb_controllers(hci_info_t hci_list[])
|
||||
{
|
||||
int num_hci = 0;
|
||||
for (int bus = 0; bus < PCI_MAX_BUS; bus++) {
|
||||
for (int dev = 0; dev < PCI_MAX_DEV; dev++) {
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
// Test for device/function present.
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
if (vendor_id != 0xffff) {
|
||||
// Test for a USB controller.
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
if (class_code == 0x0c03) {
|
||||
hci_type_t controller_type = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
if (controller_type < MAX_HCI_TYPE) {
|
||||
hci_list[num_hci].type = controller_type;
|
||||
hci_list[num_hci].bus = bus;
|
||||
hci_list[num_hci].dev = dev;
|
||||
hci_list[num_hci].func = func;
|
||||
num_hci++;
|
||||
// If we've filled the table, abort now.
|
||||
if (num_hci == MAX_HCI) {
|
||||
return num_hci;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Break out if this is a single function device.
|
||||
if (func == 0 && (hdr_type & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Break out if no device is present.
|
||||
if (func == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_hci;
|
||||
}
|
||||
|
||||
static void reset_usb_controller(hci_info_t *hci)
|
||||
{
|
||||
hci_type_t controller_type = hci->type;
|
||||
|
||||
int bus = hci->bus;
|
||||
int dev = hci->dev;
|
||||
int func = hci->func;
|
||||
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint16_t device_id = pci_config_read16(bus, dev, func, 0x02);
|
||||
uint16_t pci_status = pci_config_read16(bus, dev, func, 0x06);
|
||||
|
@ -380,26 +441,33 @@ static void probe_usb_controller(int bus, int dev, int func, hci_type_t controll
|
|||
// Restore access to the device and set the bus master flag in case the BIOS hasn't.
|
||||
pci_config_write16(bus, dev, func, 0x04, pci_command | (in_io_space ? 0x0005 : 0x0006));
|
||||
|
||||
hci->pm_base_addr = base_addr;
|
||||
|
||||
print_usb_info("Found %s controller %04x:%04x at %08x size %08x in %s space", hci_name[controller_type],
|
||||
(uintptr_t)vendor_id, (uintptr_t)device_id, base_addr, mmio_size, in_io_space ? "I/O" : "Mem");
|
||||
|
||||
if (in_io_space) {
|
||||
if (controller_type != UHCI) {
|
||||
print_usb_info(" Unsupported address mapping for this controller type");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (controller_type == UHCI) {
|
||||
print_usb_info(" Unsupported address mapping for this controller type");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
base_addr = map_region(base_addr, mmio_size, false);
|
||||
if (base_addr == 0) {
|
||||
print_usb_info(" Failed to map device into virtual memory");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hci->vm_base_addr = base_addr;
|
||||
|
||||
// Search for power management capability.
|
||||
//uint8_t pm_cap_ptr;
|
||||
if (pci_status & 0x10) {
|
||||
|
@ -420,22 +488,53 @@ static void probe_usb_controller(int bus, int dev, int func, hci_type_t controll
|
|||
}
|
||||
}
|
||||
|
||||
// Initialise the device according to its type.
|
||||
// Reset the device according to its type.
|
||||
bool success = false;
|
||||
switch (controller_type) {
|
||||
case UHCI:
|
||||
success = uhci_reset(bus, dev, func, base_addr);
|
||||
break;
|
||||
case OHCI:
|
||||
success = ohci_reset(base_addr);
|
||||
break;
|
||||
case EHCI:
|
||||
success = ehci_reset(bus, dev, func, base_addr);
|
||||
break;
|
||||
case XHCI:
|
||||
success = xhci_reset(base_addr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!success) {
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
}
|
||||
}
|
||||
|
||||
static void probe_usb_controller(hci_type_t controller_type, uintptr_t pm_base_addr, uintptr_t vm_base_addr)
|
||||
{
|
||||
print_usb_info("Probing %s controller at %08x", hci_name[controller_type], pm_base_addr);
|
||||
|
||||
// Probe the device according to its type.
|
||||
bool keyboards_found = false;
|
||||
if (controller_type == UHCI) {
|
||||
keyboards_found = uhci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == OHCI) {
|
||||
keyboards_found = ohci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == EHCI) {
|
||||
keyboards_found = ehci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == XHCI) {
|
||||
keyboards_found = xhci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
switch (controller_type) {
|
||||
case UHCI:
|
||||
keyboards_found = uhci_probe(vm_base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case OHCI:
|
||||
keyboards_found = ohci_probe(vm_base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case EHCI:
|
||||
keyboards_found = ehci_probe(vm_base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case XHCI:
|
||||
keyboards_found = xhci_probe(vm_base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (keyboards_found) {
|
||||
num_usb_controllers++;
|
||||
num_hcd++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,6 +555,20 @@ uint32_t usb_route(const usb_hub_t *hub, int port_num)
|
|||
return hub->route | (port_num << (4 * (hub->level - 1)));
|
||||
}
|
||||
|
||||
usb_parent_t usb_hs_parent(const usb_hub_t *hub, int port_num, usb_speed_t device_speed)
|
||||
{
|
||||
usb_parent_t hs_parent = { 0, 0 };
|
||||
if (device_speed < USB_SPEED_HIGH && hub->level > 0) {
|
||||
if (hub->ep0->device_speed < USB_SPEED_HIGH) {
|
||||
hs_parent = hub->hs_parent;
|
||||
} else {
|
||||
hs_parent.device_id = hub->ep0->device_id;
|
||||
hs_parent.port_num = port_num;
|
||||
}
|
||||
}
|
||||
return hs_parent;
|
||||
}
|
||||
|
||||
bool wait_until_clr(const volatile uint32_t *reg, uint32_t bit_mask, int max_time)
|
||||
{
|
||||
int timer = max_time >> 3;
|
||||
|
@ -543,12 +656,12 @@ bool assign_usb_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_num
|
|||
ep0->max_packet_size = default_max_packet_size(device_speed);
|
||||
ep0->interval = 0;
|
||||
|
||||
// The device should currently be in Default state. For loww and full speed devices, We first fetch the first
|
||||
// The device should currently be in Default state. For low and full speed devices, we first fetch the first
|
||||
// 8 bytes of the device descriptor to discover the maximum packet size for the control endpoint. We then set
|
||||
// the device address, which moves the device into Address state, and fetch the full device descriptor.
|
||||
|
||||
size_t fetch_length = sizeof(usb_device_desc_t);
|
||||
if (device_speed < USB_SPEED_HIGH) {
|
||||
if (device_speed < USB_SPEED_HIGH || usb_init_options & USB_2_STEP_INIT) {
|
||||
fetch_length = 8;
|
||||
goto fetch_descriptor;
|
||||
}
|
||||
|
@ -719,62 +832,40 @@ void find_usb_keyboards(bool pause_if_none)
|
|||
clear_screen();
|
||||
print_usb_info("Scanning for USB keyboards...");
|
||||
|
||||
num_usb_controllers = 0;
|
||||
for (int bus = 0; bus < PCI_MAX_BUS; bus++) {
|
||||
for (int dev = 0; dev < PCI_MAX_DEV; dev++) {
|
||||
hci_type_t controller_type[PCI_MAX_FUNC];
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
controller_type[func] = NOT_HCI;
|
||||
hci_info_t hci_list[MAX_HCI];
|
||||
|
||||
int num_hci = find_usb_controllers(hci_list);
|
||||
|
||||
// Take ownership of all controllers and reset them.
|
||||
for (int i = 0; i < num_hci; i++) {
|
||||
reset_usb_controller(&hci_list[i]);
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
// Test for device/function present.
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
if (vendor_id != 0xffff) {
|
||||
// Test for a USB controller.
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
if (class_code == 0x0c03) {
|
||||
controller_type[func] = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
// We need to initialise EHCI controllers before initialising any of their companion
|
||||
// controllers, so do it now.
|
||||
if (controller_type[func] == EHCI) {
|
||||
|
||||
num_hcd = 0;
|
||||
|
||||
// As we don't support hot plugging, we need to probe EHCI controllers before
|
||||
// probing any of their companion controllers, to ensure any low and full speed
|
||||
// devices are routed to the companion controllers before we probe them.
|
||||
for (int i = 0; i < num_hci && num_hcd < MAX_HCD; i++) {
|
||||
if (hci_list[i].type == EHCI) {
|
||||
if (~usb_init_options & USB_IGNORE_EHCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
}
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
}
|
||||
controller_type[func] = NOT_HCI; // prevent reprobing
|
||||
}
|
||||
}
|
||||
// Break out if this is a single function device.
|
||||
if (func == 0 && (hdr_type & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Break out if no device is present.
|
||||
if (func == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
if (controller_type[func] != NOT_HCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
probe_usb_controller(EHCI, hci_list[i].pm_base_addr, hci_list[i].vm_base_addr);
|
||||
}
|
||||
hci_list[i].type = NOT_HCI; // prevent this controller from being scanned again
|
||||
}
|
||||
}
|
||||
|
||||
// Now probe the other controllers.
|
||||
for (int i = 0; i < num_hci && num_hcd < MAX_HCD; i++) {
|
||||
if (hci_list[i].type != NOT_HCI) {
|
||||
probe_usb_controller(hci_list[i].type, hci_list[i].pm_base_addr, hci_list[i].vm_base_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (usb_init_options & USB_DEBUG) {
|
||||
print_usb_info("Press any key to continue...");
|
||||
while (get_key() == 0) {}
|
||||
} else if (pause_if_none && num_usb_controllers == 0) {
|
||||
} else if (pause_if_none && num_hcd == 0) {
|
||||
for (int i = PAUSE_IF_NONE_TIME; i > 0; i--) {
|
||||
print_usb_info("No USB keyboards found. Continuing in %i second%c ", i, i == 1 ? ' ' : 's');
|
||||
sleep(1);
|
||||
|
@ -785,10 +876,10 @@ void find_usb_keyboards(bool pause_if_none)
|
|||
|
||||
uint8_t get_usb_keycode(void)
|
||||
{
|
||||
for (int i = 0; i < num_usb_controllers; i++) {
|
||||
const usb_hcd_t *hcd = &usb_controllers[i];
|
||||
for (int i = 0; i < num_hcd; i++) {
|
||||
const usb_hcd_t *hcd = &hcd_list[i];
|
||||
|
||||
usb_controllers[i].methods->poll_keyboards(hcd);
|
||||
hcd->methods->poll_keyboards(hcd);
|
||||
|
||||
int kc_index_o = hcd->ws->kc_index_o;
|
||||
if (kc_index_o != hcd->ws->kc_index_i) {
|
||||
|
|
|
@ -62,6 +62,14 @@ typedef struct __attribute__ ((packed)) {
|
|||
uint8_t reserved;
|
||||
} usb_ep_t;
|
||||
|
||||
/**
|
||||
* A USB parent device descriptor (used internally by the various HCI drivers).
|
||||
*/
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint8_t device_id;
|
||||
uint8_t port_num;
|
||||
} usb_parent_t;
|
||||
|
||||
/**
|
||||
* A USB hub descriptor (used internally by the various HCI drivers).
|
||||
*/
|
||||
|
@ -72,6 +80,8 @@ typedef struct __attribute__ ((packed)) {
|
|||
uint8_t num_ports;
|
||||
uint8_t tt_think_time;
|
||||
uint8_t power_up_delay;
|
||||
usb_parent_t hs_parent;
|
||||
uint16_t reserved;
|
||||
} usb_hub_t;
|
||||
|
||||
/**
|
||||
|
@ -121,7 +131,8 @@ typedef enum {
|
|||
USB_DEFAULT_INIT = 0,
|
||||
USB_EXTRA_RESET = 1 << 0,
|
||||
USB_IGNORE_EHCI = 1 << 1,
|
||||
USB_DEBUG = 1 << 2
|
||||
USB_2_STEP_INIT = 1 << 2,
|
||||
USB_DEBUG = 1 << 3
|
||||
} usb_init_options_t;
|
||||
|
||||
/**
|
||||
|
@ -209,6 +220,17 @@ static inline bool valid_usb_config_descriptor(const uint8_t *buffer)
|
|||
*/
|
||||
uint32_t usb_route(const usb_hub_t *hub, int port_num);
|
||||
|
||||
/**
|
||||
* Returns the high-speed parent device ID and port number (as defined by
|
||||
* the EHCI and XHCI specifications) for the device attached to the hub
|
||||
* port specified by hub and port_num. Returns zero values if the device
|
||||
* is operating at high speed (as specified by device_speed) or is directly
|
||||
* attached to a root hub port.
|
||||
*
|
||||
* Used internally by the various HCI drivers.
|
||||
*/
|
||||
usb_parent_t usb_hs_parent(const usb_hub_t *hub, int port_num, usb_speed_t device_speed);
|
||||
|
||||
/**
|
||||
* Waits for all the bits set in bit_mask to be cleared in the register pointed
|
||||
* to by reg or for max_time microseconds to elapse.
|
||||
|
|
204
system/xhci.c
204
system/xhci.c
|
@ -4,9 +4,9 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "heap.h"
|
||||
#include "memrw32.h"
|
||||
#include "memsize.h"
|
||||
#include "pmem.h"
|
||||
#include "usb.h"
|
||||
#include "vmem.h"
|
||||
|
||||
|
@ -329,9 +329,11 @@ typedef struct {
|
|||
uint32_t cr_enqueue_state;
|
||||
uint32_t er_dequeue_state;
|
||||
|
||||
// Transient values used during device enumeration and configuration.
|
||||
// Input context for controller commands.
|
||||
uintptr_t input_context_addr;
|
||||
uintptr_t output_context_addr;
|
||||
|
||||
// Transient values used during device enumeration and configuration.
|
||||
uintptr_t initial_heap_mark;
|
||||
uintptr_t control_ep_tr_addr;
|
||||
uintptr_t interrupt_ep_tr_addr;
|
||||
|
||||
|
@ -342,26 +344,10 @@ typedef struct {
|
|||
uint8_t kbd_ep_id [MAX_KEYBOARDS];
|
||||
} workspace_t __attribute__ ((aligned (64)));
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// The heap segment of the physical memory map is used when allocating memory
|
||||
// to the controller that we don't need to access during normal operation.
|
||||
// Any memory we do need to access during normal operation is allocated from
|
||||
// segment 0, which is permanently mapped into the virtual memory address space.
|
||||
|
||||
static int heap_segment = -1;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t num_pages(size_t size)
|
||||
{
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static size_t round_up(size_t size, size_t alignment)
|
||||
{
|
||||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
|
@ -658,37 +644,36 @@ static int allocate_slot(const usb_hcd_t *hcd)
|
|||
|
||||
xhci_trb_t event;
|
||||
|
||||
// Allocate and initialise a private workspace for this device.
|
||||
// TODO: check for heap overflow.
|
||||
// Record the heap state to allow us to free memory.
|
||||
ws->initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
pm_map[0].end -= num_pages(DEVICE_WS_SIZE);
|
||||
uintptr_t device_workspace_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
ws->output_context_addr = device_workspace_addr;
|
||||
ws->control_ep_tr_addr = device_workspace_addr + XHCI_MAX_OP_CONTEXT_SIZE;
|
||||
ws->interrupt_ep_tr_addr = device_workspace_addr + XHCI_MAX_OP_CONTEXT_SIZE + sizeof(ep_tr_t);
|
||||
// Allocate and initialise a private workspace for this device.
|
||||
uintptr_t device_workspace_addr = heap_alloc(HEAP_TYPE_LM_1, DEVICE_WS_SIZE, PAGE_SIZE);
|
||||
if (device_workspace_addr == 0) {
|
||||
goto free_memory;
|
||||
}
|
||||
|
||||
memset((void *)device_workspace_addr, 0, DEVICE_WS_SIZE);
|
||||
|
||||
// Temporarily allocate and initialise the input context data structure on the heap.
|
||||
// As we only use this temporarily, there's no need to adjust pm_map.
|
||||
|
||||
ws->input_context_addr = device_workspace_addr - XHCI_MAX_IP_CONTEXT_SIZE;
|
||||
|
||||
memset((void *)ws->input_context_addr, 0, XHCI_MAX_IP_CONTEXT_SIZE);
|
||||
ws->control_ep_tr_addr = device_workspace_addr + XHCI_MAX_OP_CONTEXT_SIZE;
|
||||
ws->interrupt_ep_tr_addr = device_workspace_addr + XHCI_MAX_OP_CONTEXT_SIZE + sizeof(ep_tr_t);
|
||||
|
||||
// Allocate a device slot and set up its output context.
|
||||
|
||||
enqueue_xhci_command(ws, XHCI_TRB_ENABLE_SLOT | XHCI_TRB_USB2_SLOT, 0, 0);
|
||||
ring_host_controller_doorbell(ws->db_regs);
|
||||
if (wait_for_xhci_event(ws, XHCI_TRB_COMMAND_COMPLETE, 100*MILLISEC, &event) != XHCI_EVENT_CC_SUCCESS) {
|
||||
pm_map[0].end += num_pages(DEVICE_WS_SIZE);
|
||||
return 0;
|
||||
goto free_memory;
|
||||
}
|
||||
int slot_id = event_slot_id(&event);
|
||||
|
||||
write64(&ws->device_context_index[slot_id], ws->output_context_addr);
|
||||
write64(&ws->device_context_index[slot_id], device_workspace_addr);
|
||||
|
||||
return slot_id;
|
||||
|
||||
free_memory:
|
||||
heap_rewind(HEAP_TYPE_LM_1, ws->initial_heap_mark);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool release_slot(const usb_hcd_t *hcd, int slot_id)
|
||||
|
@ -705,7 +690,7 @@ static bool release_slot(const usb_hcd_t *hcd, int slot_id)
|
|||
|
||||
write64(&ws->device_context_index[slot_id], 0);
|
||||
|
||||
pm_map[0].end += num_pages(DEVICE_WS_SIZE);
|
||||
heap_rewind(HEAP_TYPE_LM_1, ws->initial_heap_mark);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -733,6 +718,8 @@ static bool assign_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_
|
|||
|
||||
// Prepare the input context for the ADDRESS_DEVICE command.
|
||||
|
||||
memset((xhci_input_context_t *)ws->input_context_addr, 0, sizeof(xhci_input_context_t));
|
||||
|
||||
xhci_ctrl_context_t *ctrl_context = (xhci_ctrl_context_t *)ws->input_context_addr;
|
||||
ctrl_context->add_context_flags = XHCI_CONTEXT_A(0) | XHCI_CONTEXT_A(1);
|
||||
|
||||
|
@ -742,13 +729,13 @@ static bool assign_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_
|
|||
uint32_t route = usb_route(hub, port_num);
|
||||
slot_context->params1 |= route & 0xfffff;
|
||||
slot_context->root_hub_port_num = route >> 24;
|
||||
if (device_speed < USB_SPEED_HIGH && hub->ep0->device_speed == USB_SPEED_HIGH) {
|
||||
slot_context->parent_slot_id = hub->ep0->device_id;
|
||||
slot_context->parent_port_num = port_num;
|
||||
}
|
||||
usb_parent_t hs_parent = usb_hs_parent(hub, port_num, device_speed);
|
||||
slot_context->parent_slot_id = hs_parent.device_id;
|
||||
slot_context->parent_port_num = hs_parent.port_num;
|
||||
} else {
|
||||
slot_context->root_hub_port_num = port_num;
|
||||
}
|
||||
|
||||
xhci_ep_context_t *ep_context = (xhci_ep_context_t *)(ws->input_context_addr + 2 * ws->context_size);
|
||||
ep_context->params2 = XHCI_EP_CONTROL << 3 | 3 << 1; // EP Type | CErr
|
||||
ep_context->max_burst_size = 0;
|
||||
|
@ -769,7 +756,7 @@ static bool assign_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_
|
|||
|
||||
size_t fetch_length = sizeof(usb_device_desc_t);
|
||||
uint32_t command_flags = 0;
|
||||
if (device_speed < USB_SPEED_HIGH) {
|
||||
if (device_speed < USB_SPEED_HIGH || usb_init_options & USB_2_STEP_INIT) {
|
||||
fetch_length = 8;
|
||||
command_flags = XHCI_TRB_BSR;
|
||||
}
|
||||
|
@ -830,7 +817,7 @@ static bool configure_interrupt_endpoint(workspace_t *ws, const usb_ep_t *ep, in
|
|||
xhci_slot_context_t *slot_context = (xhci_slot_context_t *)(ws->input_context_addr + ws->context_size);
|
||||
slot_context->params1 = ep_id << 27 | hub_flag << 26 | (slot_context->params1 & 0x00ffffff);
|
||||
slot_context->num_ports = num_ports;
|
||||
slot_context->params2 = tt_think_time;
|
||||
slot_context->params2 = ep->device_speed == USB_SPEED_HIGH ? tt_think_time : 0;
|
||||
|
||||
xhci_ep_context_t *ep_context = (xhci_ep_context_t *)(ws->input_context_addr + (1 + ep_id) * ws->context_size);
|
||||
ep_context->params1 = 0;
|
||||
|
@ -903,20 +890,6 @@ static void poll_keyboards(const usb_hcd_t *hcd)
|
|||
}
|
||||
}
|
||||
|
||||
static bool set_heap_segment(void)
|
||||
{
|
||||
// Use the largest 32-bit addressable physical memory segment for the heap.
|
||||
uintptr_t max_segment_size = 0;
|
||||
for (int i = 0; i < pm_map_size && pm_map[i].end <= PAGE_C(4,GB); i++) {
|
||||
uintptr_t segment_size = pm_map[i].end - pm_map[i].start;
|
||||
if (segment_size >= max_segment_size) {
|
||||
max_segment_size = segment_size;
|
||||
heap_segment = i;
|
||||
}
|
||||
}
|
||||
return max_segment_size > 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Driver Method Table
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -937,17 +910,8 @@ static const hcd_methods_t methods = {
|
|||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool xhci_reset(uintptr_t base_addr)
|
||||
{
|
||||
if (heap_segment < 0) {
|
||||
if (!set_heap_segment()) return false;
|
||||
}
|
||||
uintptr_t heap_segment_end = pm_map[heap_segment].end;
|
||||
|
||||
uint8_t port_type[XHCI_MAX_PORTS];
|
||||
|
||||
memset(port_type, 0, sizeof(port_type));
|
||||
|
||||
xhci_cap_regs_t *cap_regs = (xhci_cap_regs_t *)base_addr;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
|
@ -979,6 +943,44 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
timer--;
|
||||
}
|
||||
}
|
||||
ext_cap_offs = ext_cap->next_offset;
|
||||
}
|
||||
|
||||
xhci_op_regs_t *op_regs = (xhci_op_regs_t *)(base_addr + cap_regs->cap_length);
|
||||
|
||||
// Ensure the controller is halted and then reset it.
|
||||
if (!halt_host_controller(op_regs)) return false;
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xhci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
uint8_t port_type[XHCI_MAX_PORTS];
|
||||
|
||||
memset(port_type, 0, sizeof(port_type));
|
||||
|
||||
xhci_cap_regs_t *cap_regs = (xhci_cap_regs_t *)base_addr;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
xhci_cap_regs_t cap_regs_copy;
|
||||
memcpy32(&cap_regs_copy, cap_regs, sizeof(cap_regs_copy));
|
||||
cap_regs = &cap_regs_copy;
|
||||
#endif
|
||||
|
||||
// Walk the extra capabilities list.
|
||||
uintptr_t ext_cap_base = base_addr;
|
||||
uintptr_t ext_cap_offs = cap_regs->hcc_params1 >> 16;
|
||||
while (ext_cap_offs != 0) {
|
||||
ext_cap_base += ext_cap_offs * sizeof(uint32_t);
|
||||
xhci_ext_cap_t *ext_cap = (xhci_ext_cap_t *)ext_cap_base;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
xhci_ext_cap_t ext_cap_copy;
|
||||
memcpy32(&ext_cap_copy, ext_cap, sizeof(ext_cap_copy));
|
||||
ext_cap = &ext_cap_copy;
|
||||
#endif
|
||||
if (ext_cap->id == XHCI_EXT_CAP_SUPPORTED_PROTOCOL) {
|
||||
xhci_supported_protocol_t *protocol = (xhci_supported_protocol_t *)ext_cap_base;
|
||||
|
||||
|
@ -1016,14 +1018,12 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
xhci_rt_regs_t *rt_regs = (xhci_rt_regs_t *)(base_addr + cap_regs->rts_offset);
|
||||
xhci_db_reg_t *db_regs = (xhci_db_reg_t *)(base_addr + cap_regs->db_offset);
|
||||
|
||||
// Ensure the controller is halted and then reset it.
|
||||
if (!halt_host_controller(op_regs)) return false;
|
||||
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
// Record the heap states to allow us to free memory.
|
||||
uintptr_t initial_lm_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
uintptr_t initial_hm_heap_mark = heap_mark(HEAP_TYPE_HM_1);
|
||||
|
||||
// Record the controller page size.
|
||||
uintptr_t xhci_page_size = (read32(&op_regs->page_size) & 0xffff) << 12;
|
||||
uintptr_t xhci_page_mask = xhci_page_size - 1;
|
||||
|
||||
// Find the maximum number of device slots the controller supports.
|
||||
int max_slots = cap_regs->hcs_params1 & 0xff;
|
||||
|
@ -1033,31 +1033,30 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
| ((cap_regs->hcs_params2 >> 27) & 0x1f);
|
||||
|
||||
// Allocate and clear the scratchpad memory on the heap. This must be aligned to the controller page size.
|
||||
// TODO: check for heap overflow.
|
||||
uintptr_t scratchpad_size = num_scratchpad_buffers * xhci_page_size;
|
||||
pm_map[heap_segment].end -= num_pages(scratchpad_size);
|
||||
pm_map[heap_segment].end &= ~(xhci_page_mask >> PAGE_SHIFT);
|
||||
uintptr_t scratchpad_paddr = pm_map[heap_segment].end << PAGE_SHIFT;
|
||||
uintptr_t scratchpad_paddr = heap_alloc(HEAP_TYPE_HM_1, scratchpad_size, xhci_page_size);
|
||||
if (scratchpad_paddr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
uintptr_t scratchpad_vaddr = map_region(scratchpad_paddr, scratchpad_size, true);
|
||||
if (scratchpad_vaddr == 0) {
|
||||
pm_map[heap_segment].end = heap_segment_end;
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
memset((void *)scratchpad_vaddr, 0, scratchpad_size);
|
||||
|
||||
// Allocate and initialise the device context base address and scratchpad buffer arrays on the heap.
|
||||
// Both need to be aligned on a 64 byte boundary.
|
||||
// TODO: check for heap overflow.
|
||||
uintptr_t device_context_index_size = (1 + max_slots) * sizeof(uint64_t);
|
||||
uintptr_t scratchpad_buffer_index_offs = round_up(device_context_index_size, 64);
|
||||
uintptr_t scratchpad_buffer_index_size = num_scratchpad_buffers * sizeof(uint64_t);
|
||||
pm_map[heap_segment].end -= num_pages(scratchpad_buffer_index_offs + scratchpad_buffer_index_size);
|
||||
uintptr_t device_context_index_paddr = pm_map[heap_segment].end << PAGE_SHIFT;
|
||||
uintptr_t device_context_index_paddr = heap_alloc(HEAP_TYPE_HM_1, scratchpad_buffer_index_offs + scratchpad_buffer_index_size, 64);
|
||||
if (device_context_index_paddr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
uintptr_t device_context_index_vaddr = map_region(device_context_index_paddr, scratchpad_buffer_index_offs + scratchpad_buffer_index_size, true);
|
||||
if (device_context_index_vaddr == 0) {
|
||||
pm_map[heap_segment].end = heap_segment_end;
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
memset((void *)device_context_index_vaddr, 0, device_context_index_size);
|
||||
|
@ -1073,11 +1072,11 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
}
|
||||
}
|
||||
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory,
|
||||
// so allocate it in the first segment.
|
||||
// TODO: check for segment overflow.
|
||||
pm_map[0].end -= num_pages(sizeof(workspace_t));
|
||||
uintptr_t workspace_addr = pm_map[0].end << PAGE_SHIFT;
|
||||
// Allocate and initialise a workspace for this controller. This needs to be permanently mapped into virtual memory.
|
||||
uintptr_t workspace_addr = heap_alloc(HEAP_TYPE_LM_1, sizeof(workspace_t), PAGE_SIZE);
|
||||
if (workspace_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
workspace_t *ws = (workspace_t *)workspace_addr;
|
||||
|
||||
memset(ws, 0, sizeof(workspace_t));
|
||||
|
@ -1093,6 +1092,14 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
ws->cr_enqueue_state = WS_CR_SIZE; // cycle = 1, index = 0
|
||||
ws->er_dequeue_state = WS_ER_SIZE; // cycle = 1, index = 0
|
||||
|
||||
// Allocate and initialise the input context data structure. This needs to be contained within a single page.
|
||||
ws->input_context_addr = heap_alloc(HEAP_TYPE_LM_1, XHCI_MAX_IP_CONTEXT_SIZE, PAGE_SIZE);
|
||||
if (ws->input_context_addr == 0) {
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
memset((void *)ws->input_context_addr, 0, XHCI_MAX_IP_CONTEXT_SIZE);
|
||||
|
||||
// Initialise the driver object for this controller.
|
||||
hcd->methods = &methods;
|
||||
hcd->ws = &ws->base_ws;
|
||||
|
@ -1110,18 +1117,14 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
write64(&op_regs->dcbaap, device_context_index_paddr);
|
||||
write32(&op_regs->config, (read32(&op_regs->config) & 0xfffffc00) | max_slots);
|
||||
if (!start_host_controller(op_regs)) {
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
pm_map[heap_segment].end = heap_segment_end;
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
// Construct a hub descriptor for the root hub.
|
||||
usb_hub_t root_hub;
|
||||
memset(&root_hub, 0, sizeof(root_hub));
|
||||
root_hub.ep0 = NULL;
|
||||
root_hub.level = 0;
|
||||
root_hub.route = 0;
|
||||
root_hub.num_ports = cap_regs->hcs_params1 & 0xff;
|
||||
root_hub.power_up_delay = 0;
|
||||
|
||||
usleep(100*MILLISEC); // USB maximum device attach time.
|
||||
|
||||
|
@ -1177,16 +1180,8 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
num_keyboards, num_keyboards != 1 ? "s" : "");
|
||||
|
||||
if (num_keyboards == 0) {
|
||||
// Halt the host controller.
|
||||
(void)halt_host_controller(op_regs);
|
||||
|
||||
// Free the pages we allocated in segment 0.
|
||||
pm_map[0].end += num_pages(sizeof(workspace_t));
|
||||
|
||||
// Free the pages we allocated in the heap segment.
|
||||
pm_map[heap_segment].end = heap_segment_end;
|
||||
|
||||
return false;
|
||||
goto no_keyboards_found;
|
||||
}
|
||||
|
||||
// Initialise the interrupt TRB ring for each keyboard interface.
|
||||
|
@ -1200,4 +1195,9 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
|||
}
|
||||
|
||||
return true;
|
||||
|
||||
no_keyboards_found:
|
||||
heap_rewind(HEAP_TYPE_LM_1, initial_lm_heap_mark);
|
||||
heap_rewind(HEAP_TYPE_HM_1, initial_hm_heap_mark);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,32 @@
|
|||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the XHCI device found at base_addr, scans all the attached USB
|
||||
* devices, and configures any HID USB keyboard devices it finds to generate
|
||||
* periodic interrupt transfers that report key presses. Initialises hcd and
|
||||
* returns true if the device was successfully initialised and one or more
|
||||
* keyboards were found.
|
||||
* If necessary, takes ownership of the XHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool xhci_reset(uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the XHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool xhci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // XHCI_H
|
||||
|
|
|
@ -39,6 +39,7 @@ int test_block_move(int my_cpu, int iterations)
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t));
|
||||
if ((end - start) < 15) continue; // we need at least 16 words for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -89,6 +90,7 @@ int test_block_move(int my_cpu, int iterations)
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t));
|
||||
if ((end - start) < 15) continue; // we need at least 16 words for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -201,6 +203,7 @@ int test_block_move(int my_cpu, int iterations)
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t));
|
||||
if ((end - start) < 15) continue; // we need at least 16 words for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
|
|
@ -39,6 +39,7 @@ int test_modulo_n(int my_cpu, int iterations, testword_t pattern1, testword_t pa
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, sizeof(testword_t));
|
||||
if ((end - start) < (n - 1)) continue; // we need at least n words for this test
|
||||
end -= n; // avoids pointer overflow when incrementing p
|
||||
|
||||
testword_t *p = start + offset; // we assume each chunk has at least 'n' words, so this won't overflow
|
||||
|
@ -71,6 +72,7 @@ int test_modulo_n(int my_cpu, int iterations, testword_t pattern1, testword_t pa
|
|||
for (int j = 0; j < vm_map_size; j++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if ((end - start) < (n - 1)) continue; // we need at least n words for this test
|
||||
|
||||
int k = 0;
|
||||
testword_t *p = start;
|
||||
|
@ -111,6 +113,7 @@ int test_modulo_n(int my_cpu, int iterations, testword_t pattern1, testword_t pa
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, sizeof(testword_t));
|
||||
if ((end - start) < (n - 1)) continue; // we need at least n words for this test
|
||||
end -= n; // avoids pointer overflow when incrementing p
|
||||
|
||||
testword_t *p = start + offset; // we assume each chunk has at least 'offset' words, so this won't overflow
|
||||
|
|
|
@ -41,6 +41,7 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -99,6 +100,7 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
|||
for (int j = 0; j < vm_map_size; j++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -134,6 +136,7 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
|||
for (int j = vm_map_size - 1; j >= 0; j--) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = end;
|
||||
testword_t *ps = end;
|
||||
|
|
|
@ -51,6 +51,7 @@ int test_mov_inv_random(int my_cpu)
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -88,6 +89,7 @@ int test_mov_inv_random(int my_cpu)
|
|||
for (int j = 0; j < vm_map_size; j++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
|
|
@ -41,6 +41,7 @@ int test_mov_inv_walk1(int my_cpu, int iterations, int offset, bool inverse)
|
|||
for (int i = 0; i < vm_map_size; i++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, i, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -78,6 +79,7 @@ int test_mov_inv_walk1(int my_cpu, int iterations, int offset, bool inverse)
|
|||
for (int j = 0; j < vm_map_size; j++) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = start;
|
||||
testword_t *pe = start;
|
||||
|
@ -115,6 +117,7 @@ int test_mov_inv_walk1(int my_cpu, int iterations, int offset, bool inverse)
|
|||
for (int j = vm_map_size - 1; j >= 0; j--) {
|
||||
testword_t *start, *end;
|
||||
calculate_chunk(&start, &end, my_cpu, j, sizeof(testword_t));
|
||||
if (end < start) continue; // we need at least one word for this test
|
||||
|
||||
testword_t *p = end;
|
||||
testword_t *ps = end;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "vmem.h"
|
||||
|
||||
#include "display.h"
|
||||
#include "error.h"
|
||||
#include "test.h"
|
||||
|
@ -127,12 +129,20 @@ int test_own_addr1(int my_cpu)
|
|||
|
||||
int test_own_addr2(int my_cpu, int stage)
|
||||
{
|
||||
static testword_t offset = 0;
|
||||
static int last_stage = -1;
|
||||
|
||||
int ticks = 0;
|
||||
|
||||
offset = (stage == last_stage) ? offset + 1 : 1;
|
||||
testword_t offset;
|
||||
|
||||
// Calculate the offset (in pages) between the virtual address and the physical address.
|
||||
offset = (vm_map[0].pm_base_addr / VM_WINDOW_SIZE) * VM_WINDOW_SIZE;
|
||||
offset = (offset >= VM_PINNED_SIZE) ? offset - VM_PINNED_SIZE : 0;
|
||||
#ifdef __x86_64__
|
||||
// Convert to a byte address offset. This will translate the virtual address into a physical address.
|
||||
offset *= PAGE_SIZE;
|
||||
#else
|
||||
// Convert to a VM window offset. This will get added into the LSBs of the virtual address.
|
||||
offset /= VM_WINDOW_SIZE;
|
||||
#endif
|
||||
|
||||
switch (stage) {
|
||||
case 0:
|
||||
|
@ -145,6 +155,5 @@ int test_own_addr2(int my_cpu, int stage)
|
|||
break;
|
||||
}
|
||||
|
||||
last_stage = stage;
|
||||
return ticks;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
test_pattern_t test_list[NUM_TEST_PATTERNS] = {
|
||||
// ena, cpu, stgs, itrs, errs, description
|
||||
{ true, SEQ, 1, 6, 0, "[Address test, walking ones, no cache] "},
|
||||
{ true, SEQ, 1, 6, 0, "[Address test, own address in window] "},
|
||||
{false, SEQ, 1, 6, 0, "[Address test, own address in window] "},
|
||||
{ true, SEQ, 2, 6, 0, "[Address test, own address + window] "},
|
||||
{ true, PAR, 1, 6, 0, "[Moving inversions, 1s & 0s] "},
|
||||
{ true, PAR, 1, 3, 0, "[Moving inversions, 8 bit pattern] "},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue