Skip to content

Commit 15b8a8a

Browse files
poetteringbluca
authored andcommitted
console: when switching console modes and one doesn't work, always go for the next
So far we already had a logic in place to go for the next mode if some mode doesn't work – but it was only applied if we'd actively cycle through resolutions. Let's extend the logic and always apply it: whenever we try to switch to a mode, and it doesn't work, go to the next one until we find one that works. Fixes: #37324 (cherry picked from commit b53c3af) (cherry picked from commit 66235376a48d2d94bba6c5db0b9f5448243c2041) (cherry picked from commit 5b3ab2f8a6650841cbac5823e0c6ccbd78096c84) (cherry picked from commit b869d19) (cherry picked from commit 261ae1f)
1 parent 6b0a579 commit 15b8a8a

File tree

1 file changed

+53
-19
lines changed

1 file changed

+53
-19
lines changed

src/boot/efi/console.c

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -254,38 +254,72 @@ static int64_t get_auto_mode(void) {
254254
return CONSOLE_MODE_80_25;
255255
}
256256

257+
static int next_mode(int64_t mode, int64_t direction) {
258+
assert(IN_SET(direction, 1, -1));
259+
assert(ST->ConOut->Mode->MaxMode > 0);
260+
261+
/* Always start at the beginning if we are out of range or reached the last mode already */
262+
if (direction > 0) {
263+
if (mode < CONSOLE_MODE_RANGE_MIN || mode >= ST->ConOut->Mode->MaxMode-1)
264+
return CONSOLE_MODE_RANGE_MIN;
265+
} else if (direction < 0) {
266+
if (mode <= CONSOLE_MODE_RANGE_MIN || mode > ST->ConOut->Mode->MaxMode-1)
267+
return ST->ConOut->Mode->MaxMode-1;
268+
} else
269+
assert_not_reached();
270+
271+
return mode + direction;
272+
}
273+
257274
EFI_STATUS console_set_mode(int64_t mode) {
275+
EFI_STATUS r;
276+
277+
/* If there are no modes defined, fail immediately */
278+
if (ST->ConOut->Mode->MaxMode <= 0)
279+
return mode == CONSOLE_MODE_KEEP ? EFI_SUCCESS : EFI_UNSUPPORTED;
280+
281+
int64_t target, direction = 1;
258282
switch (mode) {
259283
case CONSOLE_MODE_KEEP:
260284
/* If the firmware indicates the current mode is invalid, change it anyway. */
261-
if (ST->ConOut->Mode->Mode < CONSOLE_MODE_RANGE_MIN)
262-
return change_mode(CONSOLE_MODE_RANGE_MIN);
263-
return EFI_SUCCESS;
285+
if (ST->ConOut->Mode->Mode >= CONSOLE_MODE_RANGE_MIN &&
286+
ST->ConOut->Mode->Mode < ST->ConOut->Mode->MaxMode)
287+
return EFI_SUCCESS;
264288

265-
case CONSOLE_MODE_NEXT:
266-
if (ST->ConOut->Mode->MaxMode <= CONSOLE_MODE_RANGE_MIN)
267-
return EFI_UNSUPPORTED;
268-
269-
mode = MAX(CONSOLE_MODE_RANGE_MIN, ST->ConOut->Mode->Mode);
270-
do {
271-
mode = (mode + 1) % ST->ConOut->Mode->MaxMode;
272-
if (change_mode(mode) == EFI_SUCCESS)
273-
break;
274-
/* If this mode is broken/unsupported, try the next.
275-
* If mode is 0, we wrapped around and should stop. */
276-
} while (mode > CONSOLE_MODE_RANGE_MIN);
289+
target = CONSOLE_MODE_RANGE_MIN;
290+
break;
277291

278-
return EFI_SUCCESS;
292+
case CONSOLE_MODE_NEXT:
293+
target = next_mode(ST->ConOut->Mode->Mode, direction);
294+
break;
279295

280296
case CONSOLE_MODE_AUTO:
281-
return change_mode(get_auto_mode());
297+
target = get_auto_mode();
298+
break;
282299

283300
case CONSOLE_MODE_FIRMWARE_MAX:
284301
/* Note: MaxMode is the number of modes, not the last mode. */
285-
return change_mode(ST->ConOut->Mode->MaxMode - 1LL);
302+
target = ST->ConOut->Mode->MaxMode - 1;
303+
direction = -1; /* search backwards for a working mode */
304+
break;
305+
306+
case CONSOLE_MODE_RANGE_MIN...CONSOLE_MODE_RANGE_MAX:
307+
target = mode;
308+
break;
286309

287310
default:
288-
return change_mode(mode);
311+
assert_not_reached();
312+
}
313+
314+
for (int64_t attempt = 0;; attempt++) {
315+
r = change_mode(target);
316+
if (r == EFI_SUCCESS)
317+
return EFI_SUCCESS;
318+
if (attempt >= ST->ConOut->Mode->MaxMode-1) /* give up, once we tried them all */
319+
return r;
320+
321+
/* If this mode is broken/unsupported, try the next. */
322+
target = next_mode(target, direction);
289323
}
290324
}
291325

0 commit comments

Comments
 (0)